Author: Dhanush Kobal

Project Name: Regression analysis

We are going to do another data analysis on house price data and see how accurate our analysis will be

Evaluation metric: \(\sqrt{\frac{1}{n} \sum_{i=1}^n (log(y_{obs}+1)-log(y_{pred}+1))^2}\)

We will assume that the train data is a good representative of the test data

Loading required package: ParamHelpers
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
Warning message: 'mlr' is in 'maintenance-only' mode since July 2019. Future development will only
happen in 'mlr3' (<https://mlr3.mlr-org.com>). Due to the focus on 'mlr3' there might be uncaught
bugs meanwhile in {mlr} - please consider switching.
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ───────────────────────────────────────────────────────────────────── tidyverse 1.3.1 ──
✓ ggplot2 3.3.5     ✓ purrr   0.3.4
✓ tibble  3.1.3     ✓ dplyr   1.0.7
✓ tidyr   1.1.3     ✓ stringr 1.4.0
✓ readr   2.0.0     ✓ forcats 0.5.1
── Conflicts ──────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
x dplyr::filter() masks stats::filter()
x dplyr::lag()    masks stats::lag()

Attaching package: ‘FSelectorRcpp’

The following object is masked from ‘package:FSelector’:

    relief

We will first see how the data is presented

Rows: 1460 Columns: 81
── Column specification ──────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (43): MSZoning, Street, Alley, LotShape, LandContour, Utilities, LotConfig, LandSlope, Neighborhood, C...
dbl (38): Id, MSSubClass, LotFrontage, LotArea, OverallQual, OverallCond, YearBuilt, YearRemodAdd, MasVnrA...

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
Rows: 1459 Columns: 80
── Column specification ──────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (43): MSZoning, Street, Alley, LotShape, LandContour, Utilities, LotConfig, LandSlope, Neighborhood, C...
dbl (37): Id, MSSubClass, LotFrontage, LotArea, OverallQual, OverallCond, YearBuilt, YearRemodAdd, MasVnrA...

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
Rows: 1459 Columns: 2
── Column specification ──────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
dbl (2): Id, SalePrice

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
[1] 1460
[1] 1459
Warning: It is deprecated to specify `guide = FALSE` to remove a guide. Please use `guide = "none"` instead.

We will look at all the numerical variables to first see if we need to convert them into factors


A preview of our updated data


Outlier analysis

We will see each numeric variables and see if we need to delete any observation

We will see how representative the train data is with the test data, so I will create factor that tells us which observation is a test data or train data

Outlier analysis: Lotarea

Outlier analysis: MasVnrArea

Warning: Removed 23 rows containing missing values (geom_point).

[1] 298
Warning: Removed 23 rows containing missing values (geom_point).

Outlier analysis: BsmtFinSF1

Warning: Removed 1 rows containing missing values (geom_point).

[1] 1299
Warning: Removed 1 rows containing missing values (geom_point).

Outlier analysis: X3SsnPorch

We removed 1 outlier

[1] 206

The rest of the data seems like a good representative of the test data

We are first going impute the dataset to do further analysis.


Data imputation

We will impute the numerical variables using the Mode and we will impute the categorical variable with a ML Algorithm (naiveBayes)

Now it is important for us to see if the levels in the test and train data are the same.

Now we will check how well the imputation went

The imputed values seems reasonable. Yes this does produce a bias in our model, however, this bias might lead to better performance


Feature engineering

Feature engineering: Street

Feature engineering: LotShape

ggplot(data = combined.data[train.kaggle$Id, ] , aes(x = LotShape, y = OverallQual)) + geom_bar(stat = "identity")


a<-ifelse(combined.data$LotShape=="Reg" , "good.overallQual",
          ifelse(combined.data$LotShape %in% c("IR1") , "medium.overallQual" , "bad.OverallQual"))

combined.data$LotArea.OverallQual.factor<-as.factor(a)

Feature engineering: Neighborhood

Feature engineering: OverQuall

Feature engineering: OverCond

Feature engineering: TotRmsAbvGrd


Quantitative feature engineering

TSNE

[1] -31.35391  19.75975 -23.86412 -28.19424 -10.59565 -10.24031

Other numerical factors that might be useful

Correlation Plot


We will see if are any data imbalances

$MSSubClass
x
  20   30   40   45   50   60   70   75   80   85   90  120  150  160  180  190 
0.37 0.05 0.00 0.01 0.10 0.20 0.04 0.01 0.04 0.01 0.04 0.06 0.00 0.04 0.01 0.02 

$MSZoning
x
C (all)      FV      RH      RL      RM 
   0.01    0.04    0.01    0.79    0.15 

$Street
x
Grvl Pave 
   0    1 

$LotShape
x
 IR1  IR2  IR3  Reg 
0.33 0.03 0.01 0.64 

$LandContour
x
 Bnk  HLS  Low  Lvl 
0.04 0.03 0.02 0.90 

$Utilities
x
AllPub NoSeWa 
     1      0 

$LotConfig
x
 Corner CulDSac     FR2     FR3  Inside 
   0.18    0.06    0.03    0.00    0.72 

$LandSlope
x
 Gtl  Mod  Sev 
0.95 0.04 0.01 

$Neighborhood
x
Blmngtn Blueste  BrDale BrkSide ClearCr CollgCr Crawfor Edwards Gilbert  IDOTRR MeadowV Mitchel   NAmes 
   0.01    0.00    0.01    0.04    0.02    0.10    0.04    0.07    0.05    0.03    0.01    0.03    0.15 
NoRidge NPkVill NridgHt  NWAmes OldTown  Sawyer SawyerW Somerst StoneBr   SWISU  Timber Veenker 
   0.03    0.01    0.05    0.05    0.08    0.05    0.04    0.06    0.02    0.02    0.02    0.01 

$Condition1
x
Artery  Feedr   Norm   PosA   PosN   RRAe   RRAn   RRNe   RRNn 
  0.03   0.06   0.86   0.01   0.01   0.01   0.02   0.00   0.00 

$Condition2
x
Artery  Feedr   Norm   PosA   PosN   RRAe   RRAn   RRNn 
  0.00   0.00   0.99   0.00   0.00   0.00   0.00   0.00 

$BldgType
x
  1Fam 2fmCon Duplex  Twnhs TwnhsE 
  0.84   0.02   0.04   0.03   0.08 

$HouseStyle
x
1.5Fin 1.5Unf 1Story 2.5Fin 2.5Unf 2Story SFoyer   SLvl 
  0.10   0.01   0.50   0.01   0.01   0.30   0.03   0.04 

$OverallQual
x
   1    2    3    4    5    6    7    8    9   10 
0.00 0.00 0.01 0.08 0.27 0.26 0.22 0.12 0.03 0.01 

$OverallCond
x
   1    2    3    4    5    6    7    8    9 
0.00 0.00 0.02 0.04 0.56 0.17 0.14 0.05 0.02 

$RoofStyle
x
   Flat   Gable Gambrel     Hip Mansard    Shed 
   0.01    0.78    0.01    0.19    0.00    0.00 

$Exterior1st
x
AsbShng AsphShn BrkComm BrkFace  CBlock CemntBd HdBoard ImStucc MetalSd Plywood   Stone  Stucco VinylSd 
   0.01    0.00    0.00    0.03    0.00    0.04    0.15    0.00    0.15    0.07    0.00    0.02    0.35 
Wd Sdng WdShing 
   0.14    0.02 

$Exterior2nd
x
AsbShng AsphShn Brk Cmn BrkFace  CBlock CmentBd HdBoard ImStucc MetalSd   Other Plywood   Stone  Stucco 
   0.01    0.00    0.00    0.02    0.00    0.04    0.14    0.01    0.15    0.00    0.10    0.00    0.02 
VinylSd Wd Sdng Wd Shng 
   0.35    0.14    0.03 

$MasVnrType
x
 BrkCmn BrkFace    None   Stone 
   0.01    0.31    0.59    0.09 

$ExterQual
x
  Ex   Fa   Gd   TA 
0.04 0.01 0.33 0.62 

$ExterCond
x
  Ex   Fa   Gd   Po   TA 
0.00 0.02 0.10 0.00 0.88 

$Foundation
x
BrkTil CBlock  PConc   Slab  Stone   Wood 
  0.10   0.43   0.44   0.02   0.00   0.00 

$BsmtQual
x
  Ex   Fa   Gd   TA 
0.08 0.03 0.42 0.47 

$BsmtCond
x
  Fa   Gd   Po   TA 
0.04 0.04 0.00 0.91 

$BsmtExposure
x
  Av   Gd   Mn   No 
0.15 0.09 0.08 0.68 

$BsmtFinType1
x
 ALQ  BLQ  GLQ  LwQ  Rec  Unf 
0.15 0.10 0.29 0.06 0.10 0.30 

$BsmtFinType2
x
 ALQ  BLQ  GLQ  LwQ  Rec  Unf 
0.01 0.02 0.01 0.03 0.04 0.88 

$Heating
x
Floor  GasA  GasW  Grav  OthW  Wall 
 0.00  0.98  0.01  0.00  0.00  0.00 

$HeatingQC
x
  Ex   Fa   Gd   Po   TA 
0.51 0.03 0.17 0.00 0.29 

$CentralAir
x
   N    Y 
0.07 0.93 

$Electrical
x
FuseA FuseF FuseP   Mix SBrkr 
 0.06  0.02  0.00  0.00  0.91 

$BsmtFullBath
x
   0    1    2    3 
0.59 0.40 0.01 0.00 

$BsmtHalfBath
x
   0    1    2 
0.94 0.05 0.00 

$HalfBath
x
   0    1    2 
0.62 0.37 0.01 

$BedroomAbvGr
x
   0    1    2    3    4    5    6    8 
0.00 0.03 0.25 0.55 0.15 0.01 0.00 0.00 

$KitchenAbvGr
x
   0    1    2    3 
0.00 0.95 0.04 0.00 

$KitchenQual
x
  Ex   Fa   Gd   TA 
0.07 0.03 0.40 0.50 

$TotRmsAbvGrd
x
   2    3    4    5    6    7    8    9   10   11   12   13   14   15 
0.00 0.01 0.07 0.19 0.28 0.22 0.13 0.05 0.03 0.01 0.01 0.00 0.00 0.00 

$Functional
x
Maj1 Maj2 Min1 Min2  Mod  Sev  Typ 
0.01 0.00 0.02 0.02 0.01 0.00 0.93 

$Fireplaces
x
   0    1    2    3    4 
0.47 0.45 0.08 0.00 0.00 

$GarageType
x
 2Types  Attchd Basment BuiltIn CarPort  Detchd 
   0.01    0.60    0.01    0.06    0.01    0.31 

$GarageFinish
x
 Fin  RFn  Unf 
0.24 0.29 0.47 

$GarageCars
x
   0    1    2    3    4    5 
0.06 0.25 0.56 0.12 0.00 0.00 

$GarageQual
x
  Ex   Fa   Gd   Po   TA 
0.00 0.07 0.01 0.00 0.92 

$GarageCond
x
  Ex   Fa   Gd   Po   TA 
0.00 0.06 0.01 0.01 0.93 

$PavedDrive
x
   N    P    Y 
0.06 0.02 0.92 

$MoSold
x
   1    2    3    4    5    6    7    8    9   10   11   12 
0.04 0.04 0.07 0.10 0.14 0.17 0.16 0.08 0.04 0.06 0.05 0.04 

$SaleType
x
  COD   Con ConLD ConLI ConLw   CWD   New   Oth    WD 
 0.03  0.00  0.01  0.00  0.00  0.00  0.08  0.00  0.87 

$SaleCondition
x
Abnorml AdjLand  Alloca  Family  Normal Partial 
   0.07    0.00    0.01    0.01    0.82    0.09 

$What.kind.of.data
x
 test.data train.data 
         0          1 

$Street.factor
x
 bad.Street good.Street 
          0           1 

$LotArea.OverallQual.factor
x
   bad.OverallQual   good.overallQual medium.overallQual 
              0.03               0.64               0.33 

$Overallqual.neigh
x
   high.overall.neigh low.overallqual.neigh med.overallqual.neigh 
                 0.20                  0.04                  0.76 

$OverallQual.salecond
x
  high.overall.saleprice    low.overall.saleprice medium.overall.saleprice 
                    0.16                     0.02                     0.83 

$OverallCond.overallqual
x
high.overall.qual  low.overall.qual  med.overall.qual 
             0.58              0.06              0.36 

$TotRmsAbvGrd.saleprice
x
high.sale.totrms  low.sale.totrms  med.sale.totrms 
            0.05             0.54             0.40 

$YearBuilt.as.factor
x
   1    2    3    4    5    6    7 
0.00 0.02 0.09 0.12 0.29 0.23 0.25 

$YearRemodAdd.as.factor
x
   1    2    3    4 
0.25 0.25 0.25 0.25 

$total_bsmt_bath
x
 1.5    2  2.5    3  3.5  4.5 
0.54 0.05 0.40 0.01 0.01 0.00 

Split the data into train and test and feature importances

Warning in makeTask(type = type, data = data, weights = weights, blocking = blocking,  :
  Empty factor levels were dropped for columns: MSSubClass,TotRmsAbvGrd,Fireplaces,GarageCars
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio

We will create various transformations for each training and test data

We will create a lasso model for feature selection

Warning in makeTask(type = type, data = data, weights = weights, blocking = blocking,  :
  Empty factor levels were dropped for columns: MSSubClass,TotRmsAbvGrd,Fireplaces,GarageCars
Resampling: cross-validation
Measures:             rmsle     
[Resample] iter 1:    0.1122575 
[Resample] iter 2:    0.1371049 
[Resample] iter 3:    0.1304579 


Aggregated Result: rmsle.test.mean=0.1266068

We will also create a bagging elastic-net model we well

Warning in makeTask(type = type, data = data, weights = weights, blocking = blocking,  :
  Empty factor levels were dropped for columns: MSSubClass,TotRmsAbvGrd,Fireplaces,GarageCars
Resampling: cross-validation
Measures:             rmsle     
[Resample] iter 1:    0.1446785 
[Resample] iter 2:    0.1231925 
[Resample] iter 3:    0.1194127 


Aggregated Result: rmsle.test.mean=0.1290946

We will also create a SVM we well

Warning in makeTask(type = type, data = data, weights = weights, blocking = blocking,  :
  Empty factor levels were dropped for columns: MSSubClass,GarageCars,TotRmsAbvGrd,Fireplaces
Resampling: cross-validation
Measures:             rmsle     
[Resample] iter 1:    0.1456177 
[Resample] iter 2:    0.1260868 
[Resample] iter 3:    0.1120551 


Aggregated Result: rmsle.test.mean=0.1279199

We will also create a SVM 2 we well. The warning can be safely ignored

Warning in makeTask(type = type, data = data, weights = weights, blocking = blocking,  :
  Empty factor levels were dropped for columns: MSSubClass,TotRmsAbvGrd,Fireplaces,GarageCars
Resampling: cross-validation
Measures:             rmsle     
[Resample] iter 1:    0.1442733 
[Resample] iter 2:    0.1266494 
[Resample] iter 3:    0.1101696 


Aggregated Result: rmsle.test.mean=0.1270308


Warning in svm.default(x, y, scale = scale, ..., na.action = na.action) :
  Variable(s) ‘X3SsnPorch’ and ‘PoolArea’ constant. Cannot scale data.
Warning in svm.default(x, y, scale = scale, ..., na.action = na.action) :
  Variable(s) ‘PoolArea’ constant. Cannot scale data.
Warning in svm.default(x, y, scale = scale, ..., na.action = na.action) :
  Variable(s) ‘PoolArea’ constant. Cannot scale data.
Warning in svm.default(x, y, scale = scale, ..., na.action = na.action) :
  Variable(s) ‘PoolArea’ constant. Cannot scale data.

We will also create a KSVM we well


Attaching package: ‘kernlab’

The following object is masked from ‘package:purrr’:

    cross

The following object is masked from ‘package:ggplot2’:

    alpha

Warning in makeTask(type = type, data = data, weights = weights, blocking = blocking,  :
  Empty factor levels were dropped for columns: MSSubClass,GarageCars,TotRmsAbvGrd,Fireplaces
Resampling: cross-validation
Measures:             rmsle     
[Resample] iter 1:    0.1323306 
[Resample] iter 2:    0.1286154 
[Resample] iter 3:    0.1267248 


Aggregated Result: rmsle.test.mean=0.1292236

We will also create a XGB we well

Resampling: cross-validation
Measures:             rmsle.test   
[Resample] iter 1:    0.2016001    
[Resample] iter 2:    0.2005690    
[Resample] iter 3:    0.2102933    


Aggregated Result: rmsle.test.mean=0.2041542

[1] 0.1984432

GLMBOOST

Resampling: cross-validation
Measures:             rmsle.test   
[Resample] iter 1:    0.1526286    
[Resample] iter 2:    0.1460310    
[Resample] iter 3:    0.1697131    
[Resample] iter 4:    0.1452754    
[Resample] iter 5:    0.1594117    


Aggregated Result: rmsle.test.mean=0.1546120

[1] 0.149486

We will also create a SVM 3 we well

Stacking: Lasso Model

We will create a LASSO model as our superlearner

The base learners were svm, and ksvm and I used a lasso model as our super-learner

Resampling: cross-validation
Measures:             rmsle.test   
[Resample] iter 1:    0.0719159    
[Resample] iter 2:    0.0263779    
[Resample] iter 3:    0.0563136    
[Resample] iter 4:    0.0316589    
[Resample] iter 5:    0.0419256    


Aggregated Result: rmsle.test.mean=0.0456384
stacked.data.train<-add_column(stacked.data.train , stacked.model = bb.train.stacked)
stacked.data.test<-add_column(stacked.data.test , stacked.model = bb.test.stacked)

Stacking: Benchmarking

set.seed(2)
svm.reg.scale<-makeLearner("regr.svm" , par.vals = list(nu = 0.5, cost = 3 , tolerance = 0.001), id = "svm.scale")
ksvm.reg<-makeLearner("regr.ksvm", par.vals = list(C = 2, epsilon = 0.001, nu = 0.2), id = "ksvm")
svm.reg.2<-makeLearner("regr.svm", par.vals = list(cost = 4, tolerance = 0.005 , nu = 0.5, scale = TRUE) , 
                       id = "svm.reg2")
svm.reg<-makeLearner("regr.svm", par.vals = list(cost = 4 , nu = 0.5 ,tolerance = 0.001 ), id = "svm")


lrns = list(svm.reg, svm.reg.2, svm.reg.scale)
train.task<-makeRegrTask(data = train.data %>% select(feat), target = "SalePrice")
Warning in makeTask(type = type, data = data, weights = weights, blocking = blocking,  :
  Empty factor levels were dropped for columns: MSSubClass,GarageCars,TotRmsAbvGrd,Fireplaces
repcv = makeResampleDesc("RepCV" , folds = 3, rep = 5)

b1<-benchmark(lrns, train.task, repcv, models = TRUE , measures = rmsle, show.info = FALSE)
Warning in makeTask(type = type, data = data, weights = weights, blocking = blocking,  :
  Empty factor levels were dropped for columns: MSSubClass,GarageCars,TotRmsAbvGrd,Fireplaces
Warning in makeTask(type = type, data = data, weights = weights, blocking = blocking,  :
  Empty factor levels were dropped for columns: MSSubClass,TotRmsAbvGrd,Fireplaces,GarageCars
Warning in makeTask(type = type, data = data, weights = weights, blocking = blocking,  :
  Empty factor levels were dropped for columns: MSSubClass,TotRmsAbvGrd,Fireplaces,GarageCars

Stacking: Benchmarking boxplots

We see that benchmarking averaged the results of all the ML models

We can create another stacking model to increase performance, or we can manually adjust the weights to see how well we do it.

No id variables; using all as measure variables

Stacking: Manuel weights based on RMSLE

It is very evident that the custom weights we have created gave us the greatest overall performance. We will use these custom weights for represent our test data as well.

No id variables; using all as measure variables

The final predictions


z<-stacked.data.test %>% select(c("svm","ksvm",  "glm.boost", "stacked.model"))

a<-0.6
b<-0
c<-0.2
d<-0.7
pp<-apply(z , 1, function(x){return(weighted.mean(x, w = c(a,b,c,d)))})
print(head(pp))
       1        2        3        4        5        6 
122418.3 157235.5 192111.2 195803.6 186949.0 173801.4 
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKPHA+KioqQXV0aG9yOiBEaGFudXNoIEtvYmFsKioqIDwvcD4KPHA+KioqUHJvamVjdCBOYW1lOiBSZWdyZXNzaW9uIGFuYWx5c2lzKioqPC9wPgo8cD5XZSBhcmUgZ29pbmcgdG8gZG8gYW5vdGhlciBkYXRhIGFuYWx5c2lzIG9uIGhvdXNlIHByaWNlIGRhdGEgYW5kIHNlZSBob3cgYWNjdXJhdGUgb3VyIGFuYWx5c2lzIHdpbGwgYmU8L3A+Cgo8cD4qKipFdmFsdWF0aW9uIG1ldHJpYzoqKiogJFxzcXJ0e1xmcmFjezF9e259IFxzdW1fe2k9MX1ebiAobG9nKHlfe29ic30rMSktbG9nKHlfe3ByZWR9KzEpKV4yfSQ8L3A+CjxwPldlIHdpbGwgYXNzdW1lIHRoYXQgdGhlICoqKnRyYWluIGRhdGEqKiogaXMgYSBnb29kIHJlcHJlc2VudGF0aXZlIG9mIHRoZSAqKip0ZXN0IGRhdGEqKio8L3A+CmBgYHtyLCBlY2hvPUZBTFNFfQpybShsaXN0PWxzKCkpCmBgYAoKYGBge3IsIGVjaG89RkFMU0V9CmxpYnJhcnkobWxyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHBhcmFsbGVsKQpsaWJyYXJ5KHBhcmFsbGVsTWFwKQpsaWJyYXJ5KEZTZWxlY3RvcikKbGlicmFyeShGU2VsZWN0b3JSY3BwKQpgYGAKCjxwPldlIHdpbGwgZmlyc3Qgc2VlIGhvdyB0aGUgZGF0YSBpcyBwcmVzZW50ZWQ8L3A+CmBgYHtyLCBlY2hvPUZBTFNFfQojIHVwbG9hZGluZyBhbmQgY29tYmluaW5nIGRhdGEKCmxpYnJhcnkocmVhZHIpCnRyYWluLmthZ2dsZSA8LSByZWFkX2NzdigiL1VzZXJzL2RoYW51c2hrb2JhbC9EZXNrdG9wLzIwMjFcIFByb2plY3RzL0thZ2dsZS9Ib3VzZVwgcHJpY2VcIHByZWRpY3Rpb24vdHJhaW4uY3N2IikKdGVzdC5rYWdnbGUgPC0gcmVhZF9jc3YoIi9Vc2Vycy9kaGFudXNoa29iYWwvRGVza3RvcC8yMDIxXCBQcm9qZWN0cy9LYWdnbGUvSG91c2VcIHByaWNlXCBwcmVkaWN0aW9uL3Rlc3QuY3N2IikKa2V5LnZhbHVlczwtIHJlYWRfY3N2KCIvVXNlcnMvZGhhbnVzaGtvYmFsL0Rlc2t0b3AvMjAyMVwgUHJvamVjdHMvS2FnZ2xlL0hvdXNlXCBwcmljZVwgcHJlZGljdGlvbi9zdWIuY3N2IikKdHJhaW4ua2FnZ2xlPC1hc190aWJibGUodHJhaW4ua2FnZ2xlKQp0ZXN0LmthZ2dsZTwtYXNfdGliYmxlKHRlc3Qua2FnZ2xlKQpucm93KHRyYWluLmthZ2dsZSk7bnJvdyh0ZXN0LmthZ2dsZSkKYW5zd2Vyczwta2V5LnZhbHVlcyRTYWxlUHJpY2UKY29tYmluZWQuZGF0YTwtbXV0YXRlKGJpbmRfcm93cyh0cmFpbi5rYWdnbGUgLCB0ZXN0LmthZ2dsZSkpICMgY29tYmluaW5nIHRyYWluIGFuZCB0ZXN0IGRhdGEKaGVhZChjb21iaW5lZC5kYXRhKQpgYGAKCmBgYHtyLCBlY2hvPUZBTFNFfQojIHJlbmFtaW5nIHZhcmlhYmxlcwoKY29sbmFtZXMoY29tYmluZWQuZGF0YSlbY29sbmFtZXMoY29tYmluZWQuZGF0YSkgJWluJSBjKCIxc3RGbHJTRiIgLCAiMm5kRmxyU0YiICwgIjNTc25Qb3JjaCIpXTwtYygiWDFzdEZsclNGIiwgICJYMm5kRmxyU0YiICwgIlgzU3NuUG9yY2giKQoKY29sbmFtZXModHJhaW4ua2FnZ2xlKVtjb2xuYW1lcyh0cmFpbi5rYWdnbGUpICVpbiUgYygiMXN0RmxyU0YiICwgIjJuZEZsclNGIiAsICIzU3NuUG9yY2giKV08LWMoIlgxc3RGbHJTRiIsICAiWDJuZEZsclNGIiAsICJYM1NzblBvcmNoIikKCmNvbG5hbWVzKHRlc3Qua2FnZ2xlKVtjb2xuYW1lcyh0ZXN0LmthZ2dsZSkgJWluJSBjKCIxc3RGbHJTRiIgLCAiMm5kRmxyU0YiICwgIjNTc25Qb3JjaCIpXTwtYygiWDFzdEZsclNGIiwgICJYMm5kRmxyU0YiICwgIlgzU3NuUG9yY2giKQpgYGAKCgpgYGB7ciwgZWNobz1GQUxTRX0KIyBwbG90dGluZyBtaXNzaW5nIHZhbHVlcwoKbmFuaWFyOjpnZ19taXNzX3Zhcihjb21iaW5lZC5kYXRhICU+JSBkcGx5cjo6c2VsZWN0KC1TYWxlUHJpY2UpKSArIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT02KSkKCmNvbWJpbmVkLmRhdGE8LWNvbWJpbmVkLmRhdGEgJT4lIGRwbHlyOjpzZWxlY3QoLWMoTG90RnJvbnRhZ2UgLEFsbGV5LCBGaXJlcGxhY2VRdSwgUG9vbFFDLEZlbmNlLCBNaXNjRmVhdHVyZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBGdWxsQmF0aCkpICMgcmVtb3ZpbmcgdmFyaWFibGVzCgpjb21iaW5lZC5kYXRhPC1jb21iaW5lZC5kYXRhICU+JSBtdXRhdGVfaWYoc2FwcGx5KGNvbWJpbmVkLmRhdGEsIGlzLmNoYXJhY3RlciksIGFzLmZhY3RvcikgCiNjb252ZXJ0aW5nIHRoaW5ncyB0byBmYWN0b3JzCmBgYAoKPHA+V2Ugd2lsbCBsb29rIGF0IGFsbCB0aGUgbnVtZXJpY2FsIHZhcmlhYmxlcyB0byBmaXJzdCBzZWUgaWYgd2UgbmVlZCB0byBjb252ZXJ0IHRoZW0gaW50byBmYWN0b3JzPC9wPgpgYGB7ciwgZWNobz1GQUxTRSwgcmVzdWx0cz0naGlkZScsZmlnLmtlZXA9J2FsbCd9CmZvcihpIGluIGNvbG5hbWVzKEZpbHRlcihpcy5udW1lcmljLCBjb21iaW5lZC5kYXRhKSkpewogIGhpc3QoY29tYmluZWQuZGF0YVt0cmFpbi5rYWdnbGUkSWQsXSAlPiUgcHVsbChpKSwgbWFpbj0gaSkKfQpjb21iaW5lZC5kYXRhPC1tdXRhdGVfYXQoY29tYmluZWQuZGF0YSwgLnZhcnMgPSBjKCJNU1N1YkNsYXNzIiwgIk92ZXJhbGxRdWFsIiwgIk92ZXJhbGxDb25kIiwgIkJzbXRGdWxsQmF0aCIsICJCc210SGFsZkJhdGgiLCAiSGFsZkJhdGgiLCAiQmVkcm9vbUFidkdyIiwgIktpdGNoZW5BYnZHciIsICJUb3RSbXNBYnZHcmQiLCAiRmlyZXBsYWNlcyIsICJHYXJhZ2VDYXJzIiwgIk1vU29sZCIpLC5mdW5zID0gYXMuZmFjdG9yKQpgYGAKCi0tLQo8cD5BIHByZXZpZXcgb2Ygb3VyICoqKnVwZGF0ZWQqKiogZGF0YTwvcD4KYGBge3IsIGVjaG89RkFMU0V9CmhlYWQoY29tYmluZWQuZGF0YSwgbj0yKQpgYGAKCi0tLQo8cCBzdHlsZT0iZm9udC1zaXplOjMwcHgiPiBPdXRsaWVyIGFuYWx5c2lzPC9wPgo8cD5XZSB3aWxsIHNlZSBlYWNoIG51bWVyaWMgdmFyaWFibGVzIGFuZCBzZWUgaWYgd2UgbmVlZCB0byBkZWxldGUgYW55IG9ic2VydmF0aW9uPC9wPgoKYGBge3IsIGVjaG89RkFMU0UsIHJlc3VsdHM9J2hpZGUnLGZpZy5rZWVwPSdhbGwnfQpmb3IoaSBpbiBjb2xuYW1lcyhGaWx0ZXIoaXMubnVtZXJpYywgY29tYmluZWQuZGF0YSkpKXsKICBwYXIobWZyb3cgPSBjKDEsMikpCiAgaGlzdChjb21iaW5lZC5kYXRhW3RyYWluLmthZ2dsZSRJZCxdICU+JSBwdWxsKGkpLCBtYWluPSBpKQogIHBsb3QoY29tYmluZWQuZGF0YVt0cmFpbi5rYWdnbGUkSWQsXSAlPiUgcHVsbChpKSAsIHkgPSBjb21iaW5lZC5kYXRhW3RyYWluLmthZ2dsZSRJZCxdJFNhbGVQcmljZSwgCiAgICAgICAgICAgICBtYWluID0gaSkKfQpgYGAKCjxwPldlIHdpbGwgc2VlIGhvdyByZXByZXNlbnRhdGl2ZSB0aGUgKioqdHJhaW4gZGF0YSoqKiBpcyB3aXRoIHRoZSAqKip0ZXN0IGRhdGEqKiosIHNvIEkgd2lsbCBjcmVhdGUgICoqKmZhY3RvcioqKiB0aGF0IHRlbGxzIHVzIHdoaWNoIG9ic2VydmF0aW9uIGlzIGEgICoqKnRlc3QgZGF0YSBvciB0cmFpbiBkYXRhKioqPC9wPgoKYGBge3IsIGVjaG89RkFMU0V9CmE8LWlmZWxzZShjb21iaW5lZC5kYXRhJElkICVpbiUgdHJhaW4ua2FnZ2xlJElkICwgInRyYWluLmRhdGEiICwgInRlc3QuZGF0YSIpCmNvbWJpbmVkLmRhdGEkV2hhdC5raW5kLm9mLmRhdGE8LWFzLmZhY3RvcihhKQpgYGAKCgo8cCBzdHlsZT0iZm9udC1zaXplOjIwcHgiPiBPdXRsaWVyIGFuYWx5c2lzOiBMb3RhcmVhPC9wPgpgYGB7ciwgZWNobz1GQUxTRX0KYTwtd2hpY2goY29tYmluZWQuZGF0YVt0cmFpbi5rYWdnbGUkSWQgLCBdJExvdEFyZWE+MTAwMDAwKQoKZ2dwbG90KGRhdGEgPSBmaWx0ZXIoY29tYmluZWQuZGF0YSksIGFlcyh4PSAxOmxlbmd0aChMb3RBcmVhKSAsIHkgPSBMb3RBcmVhKSkgKyBnZW9tX3BvaW50KCkgKyBmYWNldF93cmFwKH5XaGF0LmtpbmQub2YuZGF0YSkgKyBnZ3RpdGxlKCJPdXRsaWVyIG5vdCByZW1vdmVkIikKCmdncGxvdChkYXRhID0gZmlsdGVyKGNvbWJpbmVkLmRhdGEsICEoSWQgJWluJSBjKGEpKSksIGFlcyh4PSAxOmxlbmd0aChMb3RBcmVhKSAsIHkgPSBMb3RBcmVhKSkgKyBnZW9tX3BvaW50KCkgKyBmYWNldF93cmFwKH5XaGF0LmtpbmQub2YuZGF0YSkgKyBnZ3RpdGxlKCJPdXRsaWVyIHJlbW92ZWQiKQoKY29tYmluZWQuZGF0YTwtZmlsdGVyKGNvbWJpbmVkLmRhdGEsICEoSWQgJWluJSBjKGEpKSkKYGBgCgo8cCBzdHlsZT0iZm9udC1zaXplOjIwcHgiPiBPdXRsaWVyIGFuYWx5c2lzOiBNYXNWbnJBcmVhPC9wPgpgYGB7ciwgZWNobz1GQUxTRX0KZ2dwbG90KGRhdGEgPSBmaWx0ZXIoY29tYmluZWQuZGF0YSksIGFlcyh4PSAxOmxlbmd0aChNYXNWbnJBcmVhKSAsIHkgPSBNYXNWbnJBcmVhKSkgKyBnZW9tX3BvaW50KCkgKyBmYWNldF93cmFwKH5XaGF0LmtpbmQub2YuZGF0YSkgKyBnZ3RpdGxlKCJPdXRsaWVyIG5vdCByZW1vdmVkIikKCmE8LWNvbWJpbmVkLmRhdGEkSWRbd2hpY2goY29tYmluZWQuZGF0YVt0cmFpbi5rYWdnbGUkSWQsIF0kTWFzVm5yQXJlYT4xNTAwKV07YQoKZ2dwbG90KGRhdGEgPSBmaWx0ZXIoY29tYmluZWQuZGF0YSwgIShJZCAlaW4lIGMoYSkpKSwgYWVzKHg9IDE6bGVuZ3RoKE1hc1ZuckFyZWEpICwgeSA9IE1hc1ZuckFyZWEpKSArIGdlb21fcG9pbnQoKSArIGZhY2V0X3dyYXAofldoYXQua2luZC5vZi5kYXRhKSArIGdndGl0bGUoIk91dGxpZXIgcmVtb3ZlZCIpCgpjb21iaW5lZC5kYXRhPC1maWx0ZXIoY29tYmluZWQuZGF0YSwgIShJZCAlaW4lIGMoYSkpKQpgYGAKCjxwIHN0eWxlPSJmb250LXNpemU6MjBweCI+IE91dGxpZXIgYW5hbHlzaXM6IEJzbXRGaW5TRjE8L3A+CmBgYHtyLCBlY2hvPUZBTFNFfQpnZ3Bsb3QoZGF0YSA9IGZpbHRlcihjb21iaW5lZC5kYXRhKSwgYWVzKHg9IDE6bGVuZ3RoKEJzbXRGaW5TRjEpICwgeSA9IEJzbXRGaW5TRjEpKSArIGdlb21fcG9pbnQoKSArIGZhY2V0X3dyYXAofldoYXQua2luZC5vZi5kYXRhKSArIGdndGl0bGUoIk91dGxpZXIgbm90IHJlbW92ZWQiKQoKYTwtY29tYmluZWQuZGF0YSRJZFt3aGljaChjb21iaW5lZC5kYXRhW3RyYWluLmthZ2dsZSRJZCwgXSRCc210RmluU0YxPjQwMDApXTthCgpnZ3Bsb3QoZGF0YSA9IGZpbHRlcihjb21iaW5lZC5kYXRhLCAhKElkICVpbiUgYyhhKSkpLCBhZXMoeD0gMTpsZW5ndGgoQnNtdEZpblNGMSkgLCB5ID0gQnNtdEZpblNGMSkpICsgZ2VvbV9wb2ludCgpICsgZmFjZXRfd3JhcCh+V2hhdC5raW5kLm9mLmRhdGEpICsgZ2d0aXRsZSgiT3V0bGllciByZW1vdmVkIikKCmNvbWJpbmVkLmRhdGE8LWZpbHRlcihjb21iaW5lZC5kYXRhLCAhKElkICVpbiUgYyhhKSkpCmBgYAoKPHAgc3R5bGU9ImZvbnQtc2l6ZToyMHB4Ij4gT3V0bGllciBhbmFseXNpczogWDNTc25Qb3JjaDwvcD4KPHA+V2UgcmVtb3ZlZCAxIG91dGxpZXI8L3A+CgpgYGB7ciwgZWNobz1GQUxTRX0KZ2dwbG90KGRhdGEgPSBmaWx0ZXIoY29tYmluZWQuZGF0YSksIGFlcyh4PSAxOmxlbmd0aChYM1NzblBvcmNoKSAsIHkgPSBYM1NzblBvcmNoKSkgKyBnZW9tX3BvaW50KCkgKyBmYWNldF93cmFwKH5XaGF0LmtpbmQub2YuZGF0YSkgKyBnZ3RpdGxlKCJPdXRsaWVyIG5vdCByZW1vdmVkIikKCmE8LWNvbWJpbmVkLmRhdGEkSWRbd2hpY2goY29tYmluZWQuZGF0YVt0cmFpbi5rYWdnbGUkSWQsIF0kWDNTc25Qb3JjaD41MDApXTthCgpnZ3Bsb3QoZGF0YSA9IGZpbHRlcihjb21iaW5lZC5kYXRhLCAhKElkICVpbiUgYyhhKSkpLCBhZXMoeD0gMTpsZW5ndGgoWDNTc25Qb3JjaCkgLCB5ID0gWDNTc25Qb3JjaCkpICsgZ2VvbV9wb2ludCgpICsgZmFjZXRfd3JhcCh+V2hhdC5raW5kLm9mLmRhdGEpICsgZ2d0aXRsZSgiT3V0bGllciByZW1vdmVkIikKCmNvbWJpbmVkLmRhdGE8LWZpbHRlcihjb21iaW5lZC5kYXRhLCAhKElkICVpbiUgYyhhKSkpCmBgYAoKPHA+VGhlIHJlc3Qgb2YgdGhlIGRhdGEgc2VlbXMgbGlrZSBhIGdvb2QgcmVwcmVzZW50YXRpdmUgb2YgdGhlICoqKnRlc3QgZGF0YSoqKjwvcD4KPHA+V2UgYXJlIGZpcnN0IGdvaW5nIGltcHV0ZSB0aGUgZGF0YXNldCB0byBkbyBmdXJ0aGVyIGFuYWx5c2lzLjwvcD4KLS0tLS0KCjxwIHN0eWxlPSJmb250LXNpemU6MzBweCI+IERhdGEgaW1wdXRhdGlvbjwvcD4KPHAgc3R5bGU9ImZvbnQtc2l6ZToxNXB4Ij4gV2Ugd2lsbCBpbXB1dGUgdGhlICoqKm51bWVyaWNhbCoqKiB2YXJpYWJsZXMgdXNpbmcgdGhlICoqKk1vZGUqKiogYW5kIHdlIHdpbGwgaW1wdXRlIHRoZSBjYXRlZ29yaWNhbCB2YXJpYWJsZSB3aXRoIGEgKioqTUwgQWxnb3JpdGhtIChuYWl2ZUJheWVzKSoqKjwvcD4KCmBgYHtyLCBlY2hvPUZBTFNFfQpzZXQuc2VlZCgxKQpudW08LWltcHV0ZShhcy5kYXRhLmZyYW1lKEZpbHRlcihpcy5udW1lcmljICwgY29tYmluZWQuZGF0YSkpLCBjbGFzc2VzID0gbGlzdChudW1lcmljID0gaW1wdXRlTWVkaWFuKCkpKQpjb21iaW5lZC5kYXRhPC1jb21iaW5lZC5kYXRhICU+JSBzZWxlY3QoLVJvb2ZNYXRsKQp0cmFpbi5rYWdnbGU8LXRyYWluLmthZ2dsZSAlPiUgc2VsZWN0KC1Sb29mTWF0bCkKdGVzdC5rYWdnbGU8LXRlc3Qua2FnZ2xlICU+JSBzZWxlY3QoLVJvb2ZNYXRsKQoKZmFjPC1pbXB1dGUoYXMuZGF0YS5mcmFtZShGaWx0ZXIoaXMuZmFjdG9yICxjb21iaW5lZC5kYXRhKSksIAogICAgICAgICAgICBjbGFzc2VzID0gbGlzdChmYWN0b3IgPSBpbXB1dGVMZWFybmVyKCJjbGFzc2lmLm5haXZlQmF5ZXMiKSkpCgpjb21iaW5lZC5kYXRhPC1iaW5kX2NvbHMobnVtJGRhdGEgLGZhYyRkYXRhICkKYGBgCgo8cCBzdHlsZT0iZm9udC1zaXplOjE1cHgiPiBOb3cgaXQgaXMgaW1wb3J0YW50IGZvciB1cyB0byBzZWUgaWYgdGhlIGxldmVscyBpbiB0aGUgdGVzdCBhbmQgdHJhaW4gZGF0YSBhcmUgdGhlIHNhbWUuPC9wPgoKPHAgc3R5bGU9ImZvbnQtc2l6ZToxNXB4Ij4gTm93IHdlIHdpbGwgY2hlY2sgaG93IHdlbGwgdGhlIGltcHV0YXRpb24gd2VudDwvcD4KYGBge3IsIGVjaG89RkFMU0UsIHJlc3VsdHM9J2hpZGUnLGZpZy5rZWVwPSdhbGwnfQpmb3IoaSBpbiBjb2xuYW1lcyhGaWx0ZXIoaXMubnVtZXJpYywgY29tYmluZWQuZGF0YSkpKXsKICBwYXIobWZyb3cgPSBjKDEsMikpCiAgaGlzdCh0cmFpbi5rYWdnbGUgJT4lIGRwbHlyOjpwdWxsKGkpICwgbWFpbiA9IHBhc3RlKCJOb3QgaW1wdXRlZCIgLCBpKSkKICBoaXN0KGNvbWJpbmVkLmRhdGFbdHJhaW4ua2FnZ2xlJElkLF0gJT4lIGRwbHlyOjpwdWxsKGkpLCBtYWluID0gIkltcHV0ZWQiKQp9CmBgYAoKYGBge3IsIGVjaG89RkFMU0UsIHJlc3VsdHM9J2hpZGUnLGZpZy5rZWVwPSdhbGwnfQpmb3IgKGkgaW4gY29sbmFtZXMoRmlsdGVyKGlzLmZhY3RvciAsIGNvbWJpbmVkLmRhdGEpKSl7CiAgaWYoaSA9PSAiV2hhdC5raW5kLm9mLmRhdGEiKXticmVha30KICAKICBwYXIobWZyb3cgPSBjKDEsMikpCiAgYmFycGxvdCh0YWJsZSh0cmFpbi5rYWdnbGUgJT4lIGRwbHlyOjpwdWxsKGkpKSwgbWFpbiA9IHBhc3RlKCJOb3QgaW1wdXRlZCIgLCBpKSkKICBiYXJwbG90KHRhYmxlKGNvbWJpbmVkLmRhdGFbdHJhaW4ua2FnZ2xlJElkLF0gJT4lIGRwbHlyOjpwdWxsKGkpKSwgbWFpbiA9ICJpbXB1dGVkIikKICAKfQpgYGAKCjxwIHN0eWxlPSJmb250LXNpemU6MTVweCI+IFRoZSBpbXB1dGVkIHZhbHVlcyBzZWVtcyByZWFzb25hYmxlLiBZZXMgdGhpcyBkb2VzIHByb2R1Y2UgYSAqKipiaWFzKioqIGluIG91ciBtb2RlbCwgaG93ZXZlciwgdGhpcyBiaWFzIG1pZ2h0IGxlYWQgdG8gYmV0dGVyIHBlcmZvcm1hbmNlPC9wPgoKLS0tLS0KPHAgc3R5bGU9ImZvbnQtc2l6ZTozMHB4Ij4gRmVhdHVyZSBlbmdpbmVlcmluZzwvcD4KPHAgc3R5bGU9ImZvbnQtc2l6ZToxNXB4Ij4gRmVhdHVyZSBlbmdpbmVlcmluZzogU3RyZWV0PC9wPgoKYGBge3IsIGVjaG89RkFMU0V9CmdncGxvdChkYXRhID0gY29tYmluZWQuZGF0YVt0cmFpbi5rYWdnbGUkSWQsXSAsIGFlcyh4ID0gU3RyZWV0LCB5ID0gU2FsZVByaWNlKSkgKyBnZW9tX2JveHBsb3QoKQoKYTwtaWZlbHNlKGNvbWJpbmVkLmRhdGEkU3RyZWV0ID09ICJHcnZsIiAsICJiYWQuU3RyZWV0IiAsICJnb29kLlN0cmVldCIpCgpjb21iaW5lZC5kYXRhJFN0cmVldC5mYWN0b3I8LWFzLmZhY3RvcihhKQpgYGAKCjxwIHN0eWxlPSJmb250LXNpemU6MTVweCI+IEZlYXR1cmUgZW5naW5lZXJpbmc6IExvdFNoYXBlPC9wPgoKYGBge3J9CmdncGxvdChkYXRhID0gY29tYmluZWQuZGF0YVt0cmFpbi5rYWdnbGUkSWQsIF0gLCBhZXMoeCA9IExvdFNoYXBlLCB5ID0gT3ZlcmFsbFF1YWwpKSArIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKQoKYTwtaWZlbHNlKGNvbWJpbmVkLmRhdGEkTG90U2hhcGU9PSJSZWciICwgImdvb2Qub3ZlcmFsbFF1YWwiLAogICAgICAgICAgaWZlbHNlKGNvbWJpbmVkLmRhdGEkTG90U2hhcGUgJWluJSBjKCJJUjEiKSAsICJtZWRpdW0ub3ZlcmFsbFF1YWwiICwgImJhZC5PdmVyYWxsUXVhbCIpKQoKY29tYmluZWQuZGF0YSRMb3RBcmVhLk92ZXJhbGxRdWFsLmZhY3RvcjwtYXMuZmFjdG9yKGEpCmBgYAoKPHAgc3R5bGU9ImZvbnQtc2l6ZToxNXB4Ij4gRmVhdHVyZSBlbmdpbmVlcmluZzogTmVpZ2hib3Job29kPC9wPgoKYGBge3IsIGVjaG89RkFMU0V9CmM8LWNvbWJpbmVkLmRhdGEgJT4lIGdyb3VwX2J5KE5laWdoYm9yaG9vZCkgJT4lIHN1bW1hcmlzZShtID0gbWVhbihhcy5udW1lcmljKE92ZXJhbGxRdWFsKSkpCmdncGxvdChkYXRhID0gYyxhZXMoeCA9IE5laWdoYm9yaG9vZCAsIHkgPSBtKSkgKyBnZW9tX3BvaW50KCkgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSkKCng8LWMkTmVpZ2hib3Job29kW3doaWNoKGMkbTw9NSldCnk8LWMkTmVpZ2hib3Job29kW3doaWNoKGMkbT41ICYgYyRtPD03KV0KejwtYyROZWlnaGJvcmhvb2Rbd2hpY2goYyRtPjcpXQoKYTwtaWZlbHNlKGNvbWJpbmVkLmRhdGEkTmVpZ2hib3Job29kICVpbiUgYyhhcy5jaGFyYWN0ZXIoeCkpICwgImxvdy5vdmVyYWxscXVhbC5uZWlnaCIsCiAgICAgICAgICBpZmVsc2UoY29tYmluZWQuZGF0YSROZWlnaGJvcmhvb2QgJWluJSBjKGFzLmNoYXJhY3Rlcih5KSkgLCAibWVkLm92ZXJhbGxxdWFsLm5laWdoIiwgImhpZ2gub3ZlcmFsbC5uZWlnaCIpKQoKY29tYmluZWQuZGF0YSRPdmVyYWxscXVhbC5uZWlnaDwtYXMuZmFjdG9yKGEpCmBgYAoKPHAgc3R5bGU9ImZvbnQtc2l6ZToxNXB4Ij4gRmVhdHVyZSBlbmdpbmVlcmluZzogT3ZlclF1YWxsPC9wPgpgYGB7ciwgZWNobz1GQUxTRX0KYTwtaWZlbHNlKGFzLm51bWVyaWMoY29tYmluZWQuZGF0YSRPdmVyYWxsUXVhbCk8PTMgLCAibG93Lm92ZXJhbGwuc2FsZXByaWNlIiAsIAogICAgICAgICAgaWZlbHNlKGFzLm51bWVyaWMoY29tYmluZWQuZGF0YSRPdmVyYWxsUXVhbCk+MyAmIGFzLm51bWVyaWMoY29tYmluZWQuZGF0YSRPdmVyYWxsUXVhbCk8PTcgLCAibWVkaXVtLm92ZXJhbGwuc2FsZXByaWNlIiAsICJoaWdoLm92ZXJhbGwuc2FsZXByaWNlIikpCgpjb21iaW5lZC5kYXRhJE92ZXJhbGxRdWFsLnNhbGVjb25kPC1hcy5mYWN0b3IoYSkKYGBgCgo8cCBzdHlsZT0iZm9udC1zaXplOjE1cHgiPiBGZWF0dXJlIGVuZ2luZWVyaW5nOiBPdmVyQ29uZDwvcD4KCmBgYHtyLCBlY2hvPUZBTFNFfQpjPC1jb21iaW5lZC5kYXRhICU+JSBncm91cF9ieShPdmVyYWxsQ29uZCkgJT4lIHN1bW1hcmlzZShtID0gbWVhbihhcy5udW1lcmljKE92ZXJhbGxRdWFsKSkpCmdncGxvdChkYXRhID0gYyxhZXMoeCA9IE92ZXJhbGxDb25kICwgeSA9IG0pKSArIGdlb21fcG9pbnQoKSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApKQoKYTwtaWZlbHNlKGNvbWJpbmVkLmRhdGEkT3ZlcmFsbENvbmQgJWluJSBjKDE6NCkgLCAibG93Lm92ZXJhbGwucXVhbCIsCiAgICAgICAgICBpZmVsc2UoY29tYmluZWQuZGF0YSRPdmVyYWxsQ29uZCAlaW4lIGMoNSw5KSAsICJoaWdoLm92ZXJhbGwucXVhbCIgLCAibWVkLm92ZXJhbGwucXVhbCIpKQoKY29tYmluZWQuZGF0YSRPdmVyYWxsQ29uZC5vdmVyYWxscXVhbDwtYXMuZmFjdG9yKGEpCmBgYAoKPHAgc3R5bGU9ImZvbnQtc2l6ZToxNXB4Ij4gRmVhdHVyZSBlbmdpbmVlcmluZzogVG90Um1zQWJ2R3JkPC9wPgpgYGB7ciwgZWNobz1GQUxTRX0KYTwtaWZlbHNlKGNvbWJpbmVkLmRhdGEkVG90Um1zQWJ2R3JkICVpbiUgYygyOjYpICwgImxvdy5zYWxlLnRvdHJtcyIsIAogICAgICAgICAgaWZlbHNlKGNvbWJpbmVkLmRhdGEkVG90Um1zQWJ2R3JkICVpbiUgYyg3OjkpICwgIm1lZC5zYWxlLnRvdHJtcyIgLCAiaGlnaC5zYWxlLnRvdHJtcyIpKQoKCmNvbWJpbmVkLmRhdGEkVG90Um1zQWJ2R3JkLnNhbGVwcmljZTwtYXMuZmFjdG9yKGEpCmBgYAoKLS0tLS0KPHAgc3R5bGU9ImZvbnQtc2l6ZTozMHB4Ij4gUXVhbnRpdGF0aXZlIGZlYXR1cmUgZW5naW5lZXJpbmc8L3A+Cgo8cCBzdHlsZT0iZm9udC1zaXplOjE1cHgiPiBUU05FPC9wPgoKYGBge3IsIGVjaG89RkFMU0V9CmxpYnJhcnkoUnRzbmUpCnNldC5zZWVkKDEpClRTTkU8LXNlbGVjdChjb21iaW5lZC5kYXRhICwgLVNhbGVQcmljZSkgJT4lCiAgUnRzbmUocGVycGxleGl0eSA9IDMwKQoKY29tYmluZWQuZGF0YSR0c25lLnkxPC1UU05FJFlbLDFdCmNvbWJpbmVkLmRhdGEkdHNuZS55MjwtVFNORSRZWywyXQoKaGVhZChjb21iaW5lZC5kYXRhJHRzbmUueTEpCmBgYAoKCjxwIHN0eWxlPSJmb250LXNpemU6MTVweCI+IE90aGVyIG51bWVyaWNhbCBmYWN0b3JzIHRoYXQgbWlnaHQgYmUgdXNlZnVsPC9wPgoKYGBge3IsIGVjaG89RkFMU0V9CmNvbWJpbmVkLmRhdGEkWHN0VG90YWxGbG9vcjwtY29tYmluZWQuZGF0YSRYMXN0RmxyU0YgKyBjb21iaW5lZC5kYXRhJFgybmRGbHJTRgpjb21iaW5lZC5kYXRhJEJzbXRGaW5TRjFfMl9hZGQ8LWNvbWJpbmVkLmRhdGEkQnNtdEZpblNGMSArIGNvbWJpbmVkLmRhdGEkQnNtdEZpblNGMgpjb21iaW5lZC5kYXRhJFllYXJCdWlsdC5hcy5mYWN0b3I8LWFzLmZhY3RvcihjdXQoY29tYmluZWQuZGF0YSRZZWFyQnVpbHQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gc2VxKDE4NTAsMjAyNSwgYnkgPSAyNSksIGxhYmVscyA9IGMoMTo3KSkpCmNvbWJpbmVkLmRhdGEkWWVhclJlbW9kQWRkLmFzLmZhY3RvcjwtdW5pcXVlKGN1dChjb21iaW5lZC5kYXRhJFllYXJSZW1vZEFkZCwgc2VxKDE5MjUsIDIwMjUsIGJ5ID0gMjUpLCBsYWJlbHMgPSBjKDE6NCkpKQoKY29tYmluZWQuZGF0YSR0b3RhbF9zZjwtY29tYmluZWQuZGF0YSRCc210RmluU0YxICsgY29tYmluZWQuZGF0YSRCc210RmluU0YyICsgY29tYmluZWQuZGF0YSRYMXN0RmxyU0YrIGNvbWJpbmVkLmRhdGEkWDJuZEZsclNGCgpjb21iaW5lZC5kYXRhJHRvdGFsX2JzbXRfYmF0aDwtIGFzLmZhY3Rvcihhcy5udW1lcmljKGNvbWJpbmVkLmRhdGEkQnNtdEZ1bGxCYXRoKSArIDAuNSAqIGFzLm51bWVyaWMoY29tYmluZWQuZGF0YSRCc210SGFsZkJhdGgpKQoKY29tYmluZWQuZGF0YSR0b3RhbF9wb3JjaDwtY29tYmluZWQuZGF0YSRPcGVuUG9yY2hTRiArIGNvbWJpbmVkLmRhdGEkWDNTc25Qb3JjaCArIGNvbWJpbmVkLmRhdGEkU2NyZWVuUG9yY2ggKyBjb21iaW5lZC5kYXRhJEVuY2xvc2VkUG9yY2gKCmBgYAoKCjxwIHN0eWxlPSJmb250LXNpemU6MTVweCI+IENvcnJlbGF0aW9uIFBsb3Q8L3A+CgpgYGB7ciwgZWNobz1GQUxTRX0KY29ycnBsb3Q6OmNvcnJwbG90KGNvcihGaWx0ZXIoaXMubnVtZXJpYywgY29tYmluZWQuZGF0YSkpKQoKYGBgCgotLS0tLQoKPHAgc3R5bGU9ImZvbnQtc2l6ZToxNXB4Ij4gV2Ugd2lsbCBzZWUgaWYgYXJlIGFueSBkYXRhIGltYmFsYW5jZXM8L3A+CmBgYHtyLCBlY2hvPUZBTFNFfQphPC1GaWx0ZXIoaXMuZmFjdG9yLGZpbHRlcihjb21iaW5lZC5kYXRhLCBXaGF0LmtpbmQub2YuZGF0YSA9PSAidHJhaW4uZGF0YSIpKSAlPiUgbWFwKGZ1bmN0aW9uKHgpIHtyb3VuZChwcm9wLnRhYmxlKHRhYmxlKHgpKSwgMil9KQoKYQpgYGAKCjxwIHN0eWxlPSJmb250LXNpemU6MTVweCI+IFNwbGl0IHRoZSBkYXRhIGludG8gdHJhaW4gYW5kIHRlc3QgYW5kIGZlYXR1cmUgaW1wb3J0YW5jZXM8L3A+CgpgYGB7ciwgZWNobz1GQUxTRX0KIyBTcGxpdCB0aGUgZGF0YSBpbnRvIHRlc3QgYW5kIHRyYWluIGRhdGEKCnRyYWluLmRhdGE8LWZpbHRlcihjb21iaW5lZC5kYXRhLCBXaGF0LmtpbmQub2YuZGF0YT09InRyYWluLmRhdGEiKQp0ZXN0LmRhdGE8LWZpbHRlcihjb21iaW5lZC5kYXRhLCBXaGF0LmtpbmQub2YuZGF0YT09InRlc3QuZGF0YSIpCgp0cmFpbi5kYXRhPC0gdHJhaW4uZGF0YSAlPiUgc2VsZWN0KC1XaGF0LmtpbmQub2YuZGF0YSkKdGVzdC5kYXRhPC10ZXN0LmRhdGEgJT4lIHNlbGVjdCgtV2hhdC5raW5kLm9mLmRhdGEpCgpgYGAKCgpgYGB7ciwgZWNobz1GQUxTRX0KIyBSRiBpbXBvcnRhbmNlIHBsb3QKc2V0LnNlZWQoMSkKdHJhaW4udGFzay5mZWF0LjE8LW1ha2VSZWdyVGFzayhkYXRhID0gdHJhaW4uZGF0YSwgdGFyZ2V0ID0gIlNhbGVQcmljZSIpCmEuZmlsdGVyPC1nZW5lcmF0ZUZpbHRlclZhbHVlc0RhdGEodHJhaW4udGFzay5mZWF0LjEsIG1ldGhvZCA9ICJyYW5kb21Gb3Jlc3RTUkNfaW1wb3J0YW5jZSIpCnBsb3RGaWx0ZXJWYWx1ZXMoYS5maWx0ZXIpICsgdGhlbWVfYncoKSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdD0xKSkKYGBgCjxwIHN0eWxlPSJmb250LXNpemU6MTVweCI+IFdlIHdpbGwgY3JlYXRlIHZhcmlvdXMgKioqdHJhbnNmb3JtYXRpb25zKioqIGZvciBlYWNoIHRyYWluaW5nIGFuZCB0ZXN0IGRhdGE8L3A+CgpgYGB7ciwgZWNobz1GQUxTRX0KdHJhaW4uZGF0YS5sb2c8LXRyYWluLmRhdGEKCmYubG9nPC1mdW5jdGlvbih4KXtyZXR1cm4obG9nKHgrMSkpfQp0cmFpbi5kYXRhLmxvZzwtdHJhaW4uZGF0YS5sb2cgJT4lIG11dGF0ZShhY3Jvc3MoYyh3aGVyZShpcy5udW1lcmljKSwgLUlkLCAtU2FsZVByaWNlLCAtdHNuZS55MSwgLXRzbmUueTIpLCBmLmxvZykpCnRlc3QuZGF0YS5sb2c8LXRlc3QuZGF0YQp0ZXN0LmRhdGEubG9nPC10ZXN0LmRhdGEubG9nICU+JSBtdXRhdGUoYWNyb3NzKGMod2hlcmUoaXMubnVtZXJpYyksIC1JZCwgLXRzbmUueTEsIC10c25lLnkyKSwgZi5sb2cpKQp0ZXN0LmRhdGEubG9nJFNhbGVQcmljZTwtTkEKCmYuc3FydDwtZnVuY3Rpb24oeCl7cmV0dXJuKHNxcnQoeCkpfQp0cmFpbi5kYXRhLnJvb3Q8LXRyYWluLmRhdGEKdHJhaW4uZGF0YS5yb290PC10cmFpbi5kYXRhLnJvb3QgJT4lIG11dGF0ZShhY3Jvc3MoYyh3aGVyZShpcy5udW1lcmljKSwgLUlkLC1TYWxlUHJpY2UsIC10c25lLnkxLC10c25lLnkyKSwgZi5zcXJ0KSkKCnRlc3QuZGF0YS5yb290PC10ZXN0LmRhdGEKdGVzdC5kYXRhLnJvb3Q8LXRlc3QuZGF0YS5yb290ICU+JSBtdXRhdGUoYWNyb3NzKGMod2hlcmUoaXMubnVtZXJpYyksIC1JZCAsLVNhbGVQcmljZSwgLXRzbmUueTEsIC10c25lLnkyKSwgZi5zcXJ0KSkKdGVzdC5kYXRhLnJvb3QkU2FsZVByaWNlPC1OQQoKCnRyYWluLmRhdGEuc2NhbGU8LXRyYWluLmRhdGEgJT4lIG11dGF0ZShhY3Jvc3MoYyh3aGVyZShpcy5udW1lcmljKSwgLVNhbGVQcmljZSksIHNjYWxlKSkKdGVzdC5kYXRhLnNjYWxlPC10ZXN0LmRhdGEgJT4lIG11dGF0ZShhY3Jvc3MoYyh3aGVyZShpcy5udW1lcmljKSwgLVNhbGVQcmljZSksIHNjYWxlKSkKdHJhaW4uZGF0YS5zY2FsZSRTYWxlUHJpY2U8LXRyYWluLmRhdGEuc2NhbGUkU2FsZVByaWNlLzEwMDAwMDAKdGVzdC5kYXRhLnNjYWxlJFNhbGVQcmljZTwtTkEKCm1pbi5tYXguZnVuY3Rpb248LWZ1bmN0aW9uKHgpe3JldHVybigoeC1taW4oeCkpLyhtYXgoeCktbWluKHgpKSl9CnRyYWluLmRhdGEubWluLm1heDwtdHJhaW4uZGF0YSAlPiUgbXV0YXRlKGFjcm9zcyhjKHdoZXJlKGlzLm51bWVyaWMpLCAtU2FsZVByaWNlKSwgbWluLm1heC5mdW5jdGlvbikpCnRlc3QuZGF0YS5taW4ubWF4PC10ZXN0LmRhdGEgJT4lIG11dGF0ZShhY3Jvc3MoYyh3aGVyZShpcy5udW1lcmljKSwgLVNhbGVQcmljZSksIG1pbi5tYXguZnVuY3Rpb24pKQp0cmFpbi5kYXRhLm1pbi5tYXgkU2FsZVByaWNlPC10cmFpbi5kYXRhLm1pbi5tYXgkU2FsZVByaWNlLzEwMDAwMDAKdGVzdC5kYXRhLm1pbi5tYXgkU2FsZVByaWNlPC1OQQoKIyBzY2FsZXM6OnJlc2NhbGUodGVzdC5kYXRhLCB0bz1jKDAsMSkpCmBgYAoKCmBgYHtyLCBlY2hvPUZBTFNFfQojIGxlYXJuaW5nIGN1cnZlIGZ1bmN0aW9uCgoKcGxvdC5sZWFybmluZy5jdXJ2ZTwtZnVuY3Rpb24obW9kZWwsIGRhdGEsIGRlc2NyaXB0aW9uKXsKICBDViA9IG1ha2VSZXNhbXBsZURlc2MobWV0aG9kID0gIkNWIiwgaXRlcnMgPSA1LCBwcmVkaWN0ID0gImJvdGgiKQogIGxjMiA9IGdlbmVyYXRlTGVhcm5pbmdDdXJ2ZURhdGEobGVhcm5lcnMgPSBtb2RlbCAsIHRhc2sgPSBkYXRhLAogIHBlcmNzID0gc2VxKDAuMSwgMSwgYnkgPSAwLjEpLAogIG1lYXN1cmVzID0gbGlzdChybXNsZSwgc2V0QWdncmVnYXRpb24ocm1zbGUsIHRyYWluLm1lYW4pKSwgcmVzYW1wbGluZyA9IENWLAogIHNob3cuaW5mbyA9IEZBTFNFKQogIHByaW50KHBsb3RMZWFybmluZ0N1cnZlKGxjMiwgZmFjZXQgPSAibGVhcm5lciIpICsgdGhlbWVfYncoKSArIGdndGl0bGUobGFiZWwgPSBwYXN0ZShkZXNjcmlwdGlvbikpKQogIHJldHVybihsYzIkZGF0YSkKfQoKYGBgCgoKPHAgc3R5bGU9ImZvbnQtc2l6ZToxNXB4Ij4gV2Ugd2lsbCBjcmVhdGUgYSBsYXNzbyBtb2RlbCBmb3IgKioqZmVhdHVyZSBzZWxlY3Rpb24qKio8L3A+CgpgYGB7ciwgZWNobz1GQUxTRX0KCmZlYXQ8LWMoKGEuZmlsdGVyJGRhdGEgJT4lIGFycmFuZ2UoZGVzYyh2YWx1ZSkpKSRuYW1lWzE6NzBdLCAiU2FsZVByaWNlIikKc2V0LnNlZWQoMykKbGw8LTkwMApsYXNzby4xIDwtIG1ha2VMZWFybmVyKCJyZWdyLmdsbW5ldCIsIGFscGhhID0gMSwgaWQgPSAibGFzc28iLCBwYXIudmFscyA9IGxpc3Qocz1sbCwgc3RhbmRhcmRpemUgPVRSVUUpKQp0cmFpbi50YXNrLmxhc3NvLmZlYXQuc2VsPC1tYWtlUmVnclRhc2soZGF0YSA9IHRyYWluLmRhdGEgICwgdGFyZ2V0ID0gIlNhbGVQcmljZSIpCgpob2xkb3V0IDwtIG1ha2VSZXNhbXBsZURlc2MobWV0aG9kID0gIkNWIiwgaXRlcnMgPSAzLCBzdHJhdGlmeSA9IEZBTFNFKQpob2xkb3V0Q1YgPC1yZXNhbXBsZShsZWFybmVyID0gbGFzc28uMSwgdGFzayA9IHRyYWluLnRhc2subGFzc28uZmVhdC5zZWwsIAogICAgICAgICAgICAgICAgICAgICAgcmVzYW1wbGluZyA9IGhvbGRvdXQsIG1lYXN1cmVzID0gbGlzdChybXNsZSkpCgoKdHVuZWQubGFzc28uZmVhdC5zZWw8LXRyYWluKGxhc3NvLjEgLCB0cmFpbi50YXNrLmxhc3NvLmZlYXQuc2VsKQphPC1nZXRMZWFybmVyTW9kZWwodHVuZWQubGFzc28uZmVhdC5zZWwpCmxhc3NvQ29lZnMgPC1jb2VmKGEgLCBzID0gbGwpCgppbXBvcnRhbnQubGFzc28uZmVhdHVyZXM8LWRhdGEuZnJhbWUobGFzc28ucGFyID0gd2hpY2gobGFzc29Db2Vmc1ssMV0hPTApKQoKYTwtY29sbmFtZXModHJhaW4uZGF0YSkKYjwtY29sbmFtZXModChpbXBvcnRhbnQubGFzc28uZmVhdHVyZXMpKQoKbXlmdW4gPC0gZnVuY3Rpb24oc291cmNlX3ZlYywgZGVzdF92ZWMpIHsKICBwdXJycjo6bWFwX2RibChzb3VyY2VfdmVjLCB+IHdoaWNoKHN0cmluZ3I6OnN0cl9kZXRlY3QoLiwgZGVzdF92ZWMpKVsxXSkKfQoKaW1wb3J0YW50Lmxhc3NvLmZlYXR1cmVzPC1uYS5vbWl0KHVuaXF1ZShjb2xuYW1lcyh0cmFpbi5kYXRhKVtteWZ1bihiLCBhKV0pKQppbXBvcnRhbnQubGFzc28uZmVhdHVyZXM8LWFwcGVuZChpbXBvcnRhbnQubGFzc28uZmVhdHVyZXMgLCAiU2FsZVByaWNlIikKI2ltcG9ydGFudC5sYXNzby5mZWF0dXJlcwoKYmIudGVzdC5sYXNzby5mZWF0PC1wcmVkaWN0KHR1bmVkLmxhc3NvLmZlYXQuc2VsICwgbmV3ZGF0YSA9IHRlc3QuZGF0YSkkZGF0YSRyZXNwb25zZQoKbGVhcm5pbmcuY3VydmUubGFzc28uMTwtcGxvdC5sZWFybmluZy5jdXJ2ZShsYXNzby4xICwgdHJhaW4udGFzay5sYXNzby5mZWF0LnNlbCwgImxhc3NvIC0gdHJhaW4uZGF0YS5yb290IikKCmJiLnRyYWluLmxhc3NvLmZlYXQ8LXByZWRpY3QodHVuZWQubGFzc28uZmVhdC5zZWwsIG5ld2RhdGEgPSB0cmFpbi5kYXRhLnJvb3QpJGRhdGEkcmVzcG9uc2UKCmBgYAoKCjxwIHN0eWxlPSJmb250LXNpemU6MTVweCI+IFdlIHdpbGwgYWxzbyBjcmVhdGUgYSAgKioqYmFnZ2luZyBlbGFzdGljLW5ldCBtb2RlbCoqKiB3ZSB3ZWxsPC9wPgoKYGBge3IsIGVjaG89RkFMU0V9CgpsbDwtMTIwMApzZXQuc2VlZCgxKQplbGFzdGljLm5ldDwtbWFrZUxlYXJuZXIoInJlZ3IuZ2xtbmV0IiwgaWQgPSAiZWxhc3RpYy5uZXQiLCBwYXIudmFscyA9IGxpc3QoYWxwaGEgPSAwLjUgLCBzID0gbGwpKQpiYWcubHJuLmVuZXQgPSBtYWtlQmFnZ2luZ1dyYXBwZXIoZWxhc3RpYy5uZXQgLCBidy5pdGVycyA9IDUwLCBidy5yZXBsYWNlID0gVFJVRSwgYncuZmVhdHMgPSAwLjc1KQp0cmFpbi50YXNrLmVuZXQ8LW1ha2VSZWdyVGFzayhkYXRhID0gdHJhaW4uZGF0YSx0YXJnZXQgPSAiU2FsZVByaWNlIikKCmhvbGRvdXQgPC0gbWFrZVJlc2FtcGxlRGVzYyhtZXRob2QgPSAiQ1YiLCBpdGVycyA9IDMsIHN0cmF0aWZ5ID0gRkFMU0UpCmhvbGRvdXRDViA8LSByZXNhbXBsZShsZWFybmVyID0gYmFnLmxybi5lbmV0LCB0YXNrID0gdHJhaW4udGFzay5lbmV0LCAKICAgICAgICAgICAgICAgICAgICAgIHJlc2FtcGxpbmcgPSBob2xkb3V0LCBtZWFzdXJlcyA9IGxpc3Qocm1zbGUpKQoKdHVuZWQuYmFnZ2VkLmVuZXQ8LXRyYWluKGJhZy5scm4uZW5ldCAsIHRyYWluLnRhc2suZW5ldCkKCmJiLnRyYWluLmJhZ2dlZC5lbmV0PC1wcmVkaWN0KHR1bmVkLmJhZ2dlZC5lbmV0ICwgbmV3ZGF0YSA9IHRyYWluLmRhdGEpJGRhdGEkcmVzcG9uc2UKCmJiLnRlc3QuYmFnZ2VkLmVuZXQ8LXByZWRpY3QodHVuZWQuYmFnZ2VkLmVuZXQgLCBuZXdkYXRhID0gdGVzdC5kYXRhKSRkYXRhJHJlc3BvbnNlCgpgYGAKCgo8cCBzdHlsZT0iZm9udC1zaXplOjE1cHgiPiBXZSB3aWxsIGFsc28gY3JlYXRlIGEgICoqKlNWTSoqKiB3ZSB3ZWxsPC9wPgoKCmBgYHtyLCBlY2hvPUZBTFNFfQpwPC04MgpmZWF0PC1jKChhLmZpbHRlciRkYXRhICU+JSBhcnJhbmdlKGRlc2ModmFsdWUpKSkkbmFtZVsxOnBdLCAiU2FsZVByaWNlIikKc2V0LnNlZWQoMSkKc3ZtLnJlZzwtbWFrZUxlYXJuZXIoInJlZ3Iuc3ZtIiwgcGFyLnZhbHMgPSBsaXN0KGNvc3QgPSA0ICwgbnUgPSAwLjUgLHRvbGVyYW5jZSA9IDAuMDAxICkpCnRyYWluLnRhc2suc3ZtPC1tYWtlUmVnclRhc2soZGF0YSA9ICB0cmFpbi5kYXRhICU+JSBzZWxlY3QoZmVhdCkgICAsIHRhcmdldCA9ICJTYWxlUHJpY2UiKQoKaG9sZG91dCA8LSBtYWtlUmVzYW1wbGVEZXNjKG1ldGhvZCA9ICJDViIsIGl0ZXJzID0gMywgc3RyYXRpZnkgPSBGQUxTRSkKaG9sZG91dENWIDwtIHJlc2FtcGxlKGxlYXJuZXIgPSBzdm0ucmVnLCB0YXNrID0gdHJhaW4udGFzay5zdm0sIAogICAgICAgICAgICAgICAgICAgICAgcmVzYW1wbGluZyA9IGhvbGRvdXQsIG1lYXN1cmVzID0gbGlzdChybXNsZSkpCgp0dW5lZC5zdm08LXRyYWluKHN2bS5yZWcgLCB0cmFpbi50YXNrLnN2bSkKCmxlYXJuaW5nLmN1cnZlLnN2bS4xPC1wbG90LmxlYXJuaW5nLmN1cnZlKHN2bS5yZWcgLCB0cmFpbi50YXNrLnN2bSAsICJzdm0uMSAtIHRyYWluLmRhdGEiKQoKc3ZtPC1lMTA3MTo6c3ZtKFNhbGVQcmljZSB+IC4gLCBkYXRhID0gdHJhaW4uZGF0YSAlPiUgc2VsZWN0KGZlYXQpLCBrZXJuZWwgPSAicmFkaWFsIiwgCiAgICAgICAgICAgICAgICB0b2xlcmFuY2UgPSAwLjAwMSx0eXBlID0gImVwcy1yZWdyZXNzaW9uIiwgbnUgPSAwLjUsIGNvc3QgPSA0KQpiYi50ZXN0LnN2bTwtcHJlZGljdChzdm0gLCBuZXdkYXRhID0gdGVzdC5kYXRhJT4lIHNlbGVjdChmZWF0LCAtU2FsZVByaWNlKSkKCgpiYi50cmFpbi5zdm08LXByZWRpY3Qoc3ZtICwgbmV3ZGF0YSA9IHRyYWluLmRhdGEgJT4lIHNlbGVjdChmZWF0KSkKCmBgYAoKCjxwIHN0eWxlPSJmb250LXNpemU6MTVweCI+IFdlIHdpbGwgYWxzbyBjcmVhdGUgYSAgKioqU1ZNIDIqKiogd2Ugd2VsbC4gKioqVGhlIHdhcm5pbmcgY2FuIGJlIHNhZmVseSBpZ25vcmVkKioqPC9wPgoKYGBge3IsIGVjaG89RkFMU0V9CgpzZXQuc2VlZCgxKQpzdm0ucmVnLjI8LW1ha2VMZWFybmVyKCJyZWdyLnN2bSIsIHBhci52YWxzID0gbGlzdChjb3N0ID0gNSwgdG9sZXJhbmNlID0gMC4wMDAwMDEgLCBudSA9IDAuNSwgc2NhbGUgPSBUUlVFKSkKdHJhaW4udGFzay5zdm08LW1ha2VSZWdyVGFzayhkYXRhID0gIHRyYWluLmRhdGEgJT4lIHNlbGVjdChpbXBvcnRhbnQubGFzc28uZmVhdHVyZXMpLCB0YXJnZXQgPSAiU2FsZVByaWNlIikKCgpob2xkb3V0IDwtIG1ha2VSZXNhbXBsZURlc2MobWV0aG9kID0gIkNWIiwgaXRlcnMgPSAzLCBzdHJhdGlmeSA9IEZBTFNFKQpob2xkb3V0Q1YgPC0gcmVzYW1wbGUobGVhcm5lciA9IHN2bS5yZWcuMiwgdGFzayA9IHRyYWluLnRhc2suc3ZtLCAKICAgICAgICAgICAgICAgICAgICAgIHJlc2FtcGxpbmcgPSBob2xkb3V0LCBtZWFzdXJlcyA9IGxpc3Qocm1zbGUpKQoKbGVhcm5pbmcuY3VydmUuc3ZtLjI8LXBsb3QubGVhcm5pbmcuY3VydmUoc3ZtLnJlZy4yICwgdHJhaW4udGFzay5zdm0gLCAic3ZtLjIgLSB0cmFpbi5kYXRhICU+JSBpbXBvcnRhbnQgbGFzc28gZmVhdHVyZXMiKQoKc3ZtLjI8LWUxMDcxOjpzdm0oU2FsZVByaWNlIH4gLiAsIGRhdGEgPSB0cmFpbi5kYXRhICU+JSBzZWxlY3QoaW1wb3J0YW50Lmxhc3NvLmZlYXR1cmVzKSAsIAogICAgICAgICAgICAgICAgICBrZXJuZWwgPSAicmFkaWFsIiwgdG9sZXJhbmNlID0gMC4wMDAwMDEsdHlwZSA9ICJlcHMtcmVncmVzc2lvbiIsIG51ID0gMC41LCBjb3N0ID0gNSkKCmJiLnRlc3Quc3ZtMjwtcHJlZGljdChzdm0uMiwgbmV3ZGF0YSA9IHRlc3QuZGF0YSAlPiUgc2VsZWN0KGltcG9ydGFudC5sYXNzby5mZWF0dXJlcykpCmJiLnRyYWluLnN2bTI8LXByZWRpY3Qoc3ZtLjIsIG5ld2RhdGEgPSB0cmFpbi5kYXRhICU+JSBzZWxlY3QoaW1wb3J0YW50Lmxhc3NvLmZlYXR1cmVzKSkKCmBgYAoKPHAgc3R5bGU9ImZvbnQtc2l6ZToxNXB4Ij4gV2Ugd2lsbCBhbHNvIGNyZWF0ZSBhICAqKipLU1ZNKioqIHdlIHdlbGw8L3A+CgpgYGB7ciwgZWNobz1GQUxTRX0KCnA8LTcyCmZlYXQ8LWMoKGEuZmlsdGVyJGRhdGEgJT4lIGFycmFuZ2UoZGVzYyh2YWx1ZSkpKSRuYW1lWzE6cF0sICJTYWxlUHJpY2UiKQoKc2V0LnNlZWQoMikKbGlicmFyeShrZXJubGFiKQprc3ZtLnJlZzwtbWFrZUxlYXJuZXIoInJlZ3Iua3N2bSIsIHBhci52YWxzID0gbGlzdChDID0gMiwgZXBzaWxvbiA9IDAuMDAxLCBudSA9IDAuMiApKQp0cmFpbi50YXNrLmtzdm08LW1ha2VSZWdyVGFzayhkYXRhID0gIHRyYWluLmRhdGEgJT4lIHNlbGVjdChmZWF0KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0YXJnZXQgPSAiU2FsZVByaWNlIikKCmhvbGRvdXQgPC0gbWFrZVJlc2FtcGxlRGVzYyhtZXRob2QgPSAiQ1YiLCBpdGVycyA9IDMsIHN0cmF0aWZ5ID0gRkFMU0UpCmhvbGRvdXRDViA8LSByZXNhbXBsZShsZWFybmVyID0ga3N2bS5yZWcsIHRhc2sgPSB0cmFpbi50YXNrLmtzdm0sIAogICAgICAgICAgICAgICAgICAgICAgcmVzYW1wbGluZyA9IGhvbGRvdXQsIG1lYXN1cmVzID0gbGlzdChybXNsZSkpCgprc3ZtPC1rZXJubGFiOjprc3ZtKFNhbGVQcmljZSB+LiwgZGF0YSA9IHRyYWluLmRhdGEgJT4lIHNlbGVjdChmZWF0KSAsQyA9IDIsIGVwc2lsb24gPSAwLjAwMDEsIG51ID0wLjIgKQoKbGVhcm5pbmcuY3VydmUua3N2bTwtcGxvdC5sZWFybmluZy5jdXJ2ZShrc3ZtLnJlZyAsIHRyYWluLnRhc2sua3N2bSwgImtzdm0gLSB0cmFpbi5kYXRhICU+JSBmZWF0IikKCmJiLnRyYWluLmtzdm08LXByZWRpY3Qoa3N2bSwgbmV3ZGF0YSA9IHRyYWluLmRhdGEgJT4lIHNlbGVjdChmZWF0KSkKCmJiLnRlc3Qua3N2bTwtcHJlZGljdChrc3ZtLCBuZXdkYXRhID0gdGVzdC5kYXRhICU+JSBzZWxlY3QoZmVhdCkpCgpgYGAKCgoKPHAgc3R5bGU9ImZvbnQtc2l6ZToxNXB4Ij4gV2Ugd2lsbCBhbHNvIGNyZWF0ZSBhICAqKipYR0IqKiogd2Ugd2VsbDwvcD4KCmBgYHtyLCBlY2hvPUZBTFNFfQoKcDwtNjAKZmVhdDwtYygoYS5maWx0ZXIkZGF0YSAlPiUgYXJyYW5nZShkZXNjKHZhbHVlKSkpJG5hbWVbMTpwXSwgIlNhbGVQcmljZSIpCgpzZXQuc2VlZCgyKQp4Z2I8LW1ha2VMZWFybmVyKCJyZWdyLnhnYm9vc3QiLCBwYXIudmFscyA9IGxpc3QoZXRhID0gMSwgbWF4X2RlcHRoID0gMTAsIGV2YWxfbWV0cmljID0gJ3Jtc2xlJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW5fY2hpbGRfd2VpZ2h0ID0gNSwgZ2FtbWEgPSAxMCkpCnhnYi50YXNrPC1tYWtlUmVnclRhc2soZGF0YSA9IG11dGF0ZV9hbGwodHJhaW4uZGF0YSAlPiUgc2VsZWN0KGZlYXQpLCBhcy5udW1lcmljKSwgdGFyZ2V0ID0gIlNhbGVQcmljZSIpCgpob2xkb3V0IDwtIG1ha2VSZXNhbXBsZURlc2MobWV0aG9kID0gIkNWIiwgaXRlcnMgPSAzLCBzdHJhdGlmeSA9IEZBTFNFLCBwcmVkaWN0ID0gImJvdGgiKQpob2xkb3V0Q1YgPC0gcmVzYW1wbGUobGVhcm5lciA9IHhnYiwgdGFzayA9IHhnYi50YXNrLCAKICAgICAgICAgICAgICAgICAgICAgIHJlc2FtcGxpbmcgPSBob2xkb3V0LCBtZWFzdXJlcyA9IGxpc3Qocm1zbGUpKQoKbGVhcm5pbmcuY3VydmUueGdiPC1wbG90LmxlYXJuaW5nLmN1cnZlKHhnYiAsIHhnYi50YXNrLCAieGdiIC0gdHJhaW4uZGF0YSAlPiUgZmVhdCIpCgp0dW5lZC54Z2I8LXRyYWluKHhnYiwgeGdiLnRhc2spCgpiYi50cmFpbi54Z2I8LXByZWRpY3QodHVuZWQueGdiICwgbmV3ZGF0YSA9IHRyYWluLmRhdGEgJT4lIHNlbGVjdChmZWF0KSkKCmJiLnRlc3QueGdiPC1wcmVkaWN0KHR1bmVkLnhnYiAsIG5ld2RhdGEgPSB0ZXN0LmRhdGEgJT4lIHNlbGVjdChmZWF0KSkkZGF0YSRyZXNwb25zZQoKYGBgCgo8cCBzdHlsZT0iZm9udC1zaXplOjMwcHgiPiAqKipHTE1CT09TVCoqKjwvcD4KCgpgYGB7ciwgZWNobz1GQUxTRX0KcDwtODAKZmVhdDwtYygoYS5maWx0ZXIkZGF0YSAlPiUgYXJyYW5nZShkZXNjKHZhbHVlKSkpJG5hbWVbMTpwXSwgIlNhbGVQcmljZSIpCgpnbG0uYm9vc3Q8LW1ha2VMZWFybmVyKCJyZWdyLmdsbWJvb3N0IiwgcGFyLnZhbHMgPSBsaXN0KGNlbnRlciA9IEZBTFNFLCBtc3RvcCA9IDEwMDAwLCBudSA9IDEpKQpnbG0uYm9vc3QudGFzazwtbWFrZVJlZ3JUYXNrKGRhdGEgPSBtdXRhdGVfYWxsKHRyYWluLmRhdGEgJT4lIHNlbGVjdChmZWF0KSwgYXMubnVtZXJpYyksIHRhcmdldCA9ICJTYWxlUHJpY2UiKQoKdHVuZWQuZ2xtLmJvb3N0PC10cmFpbihnbG0uYm9vc3QsIGdsbS5ib29zdC50YXNrKQoKaG9sZG91dCA8LSBtYWtlUmVzYW1wbGVEZXNjKG1ldGhvZCA9ICJDViIsIGl0ZXJzID0gNSwgc3RyYXRpZnkgPSBGQUxTRSwgcHJlZGljdCA9ICJib3RoIikKaG9sZG91dENWIDwtIHJlc2FtcGxlKGxlYXJuZXIgPSBnbG0uYm9vc3QsIHRhc2sgPSBnbG0uYm9vc3QudGFzaywgCiAgICAgICAgICAgICAgICAgICAgICByZXNhbXBsaW5nID0gaG9sZG91dCwgbWVhc3VyZXMgPSBsaXN0KHJtc2xlKSkKCmxlYXJuaW5nLmN1cnZlLmdsbS5ib29zdDwtcGxvdC5sZWFybmluZy5jdXJ2ZSh4Z2IgLCB4Z2IudGFzaywgImdsbS5ib29zdCAtIHRyYWluLmRhdGEgJT4lIGZlYXQiKQoKYmIudHJhaW4uZ2xtLmJvb3N0PC1wcmVkaWN0KHR1bmVkLmdsbS5ib29zdCwgbmV3ZGF0YSA9IG11dGF0ZV9hbGwodHJhaW4uZGF0YSAlPiUgc2VsZWN0KGZlYXQpLCBhcy5udW1lcmljKSkkZGF0YSRyZXNwb25zZQoKYmIudGVzdC5nbG0uYm9vc3Q8LXByZWRpY3QodHVuZWQuZ2xtLmJvb3N0LCBuZXdkYXRhID0gbXV0YXRlX2FsbCh0ZXN0LmRhdGEgJT4lIHNlbGVjdChmZWF0KSwgYXMubnVtZXJpYykpJGRhdGEkcmVzcG9uc2UKCmBgYAoKCgo8cCBzdHlsZT0iZm9udC1zaXplOjE1cHgiPiBXZSB3aWxsIGFsc28gY3JlYXRlIGEgICoqKlNWTSAzKioqIHdlIHdlbGw8L3A+CgpgYGB7ciwgZWNobz1GQUxTRX0KIyBObyBMZWFybmluZyBjdXJ2ZSBvciBDViB3aWxsIGJlIG1hZGUgc2luY2UgdGhleSB3aWxsIGJlIHNpbWlsYXIgYXMgdGhlIG90aGVyIFNWTXMKCgpzdm0ucmVnLnNjYWxlPC1tYWtlTGVhcm5lcigicmVnci5zdm0iICwgcGFyLnZhbHMgPSBsaXN0KG51ID0gMC41LCBjb3N0ID0gMyAsIHRvbGVyYW5jZSA9IDAuMDAxKSkKcDwtODIKCmZlYXQ8LWMoKGEuZmlsdGVyJGRhdGEgJT4lIGFycmFuZ2UoZGVzYyh2YWx1ZSkpKSRuYW1lWzE6cF0sICJTYWxlUHJpY2UiKQoKc3ZtLnNjYWxlPC1lMTA3MTo6c3ZtKFNhbGVQcmljZSB+IC4gLCBkYXRhID0gdHJhaW4uZGF0YSAlPiUgc2VsZWN0KGZlYXQpLCBrZXJuZWwgPSAicmFkaWFsIiwgCiAgICAgICAgICAgICAgICB0b2xlcmFuY2UgPSAwLjAwMSx0eXBlID0gImVwcy1yZWdyZXNzaW9uIiwgbnUgPSAwLjUsIGNvc3QgPSAzKQoKYmIudHJhaW4uc3ZtLnNjYWxlPC1wcmVkaWN0KHN2bS5zY2FsZSwgbmV3ZGF0YSA9IHRyYWluLmRhdGEgJT4lIHNlbGVjdChmZWF0LCAtU2FsZVByaWNlKSkKYmIudGVzdC5zdm0uc2NhbGU8LXByZWRpY3Qoc3ZtLnNjYWxlLCBuZXdkYXRhID0gdGVzdC5kYXRhICU+JSBzZWxlY3QoZmVhdCwgLVNhbGVQcmljZSkpCgpgYGAKCgoKYGBge3IsIGVjaG89RkFMU0V9CiMgU3RhY2tlZCBtb2RlbCBkYXRhZnJhbWUKCnNldC5zZWVkKDEpCnN0YWNrZWQuZGF0YS50cmFpbjwtZGF0YS5mcmFtZShiYi50cmFpbi5sYXNzby5mZWF0LCBiYi50cmFpbi5iYWdnZWQuZW5ldCwgYmIudHJhaW4uc3ZtLCBiYi50cmFpbi5zdm0yLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJiLnRyYWluLmtzdm1bLDFdICwgYmIudHJhaW4ueGdiJGRhdGEkcmVzcG9uc2UgLCBiYi50cmFpbi5nbG0uYm9vc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBiYi50cmFpbi5zdm0uc2NhbGUgLCB0cmFpbi5kYXRhJFNhbGVQcmljZSkKY29sbmFtZXMoc3RhY2tlZC5kYXRhLnRyYWluKTwtYygibGFzc28iLCAiYmFnZ2VkLmVuZXQiICwgInN2bSIgLCAic3ZtLjIiICwgImtzdm0iICwgInhnYiIgLCAiZ2xtLmJvb3N0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3ZtLnNjYWxlIiwiU2FsZVByaWNlIikKCnN0YWNrZWQuZGF0YS50ZXN0PC1kYXRhLmZyYW1lKGJiLnRlc3QubGFzc28uZmVhdCAsIGJiLnRlc3QuYmFnZ2VkLmVuZXQgLCBiYi50ZXN0LnN2bSAsIGJiLnRlc3Quc3ZtMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmIudGVzdC5rc3ZtWywxXSAsIGJiLnRlc3QueGdiLCBiYi50ZXN0LmdsbS5ib29zdCwgYmIudGVzdC5zdm0uc2NhbGUpCmNvbG5hbWVzKHN0YWNrZWQuZGF0YS50ZXN0KTwtYygibGFzc28iLCAiYmFnZ2VkLmVuZXQiICwgInN2bSIgLCAic3ZtLjIiICwgImtzdm0iICwgInhnYiIgLCAiZ2xtLmJvb3N0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzdm0uc2NhbGUiKQpgYGAKCgoKPHAgc3R5bGU9ImZvbnQtc2l6ZTozMHB4Ij4gU3RhY2tpbmc6IExhc3NvIE1vZGVsPC9wPgo8cCBzdHlsZT0iZm9udC1zaXplOjE1cHgiPiBXZSB3aWxsIGNyZWF0ZSBhICoqKkxBU1NPKioqIG1vZGVsIGFzIG91ciAqKipzdXBlcmxlYXJuZXIqKio8L3A+CjxwIHN0eWxlPSJmb250LXNpemU6MTVweCI+IFRoZSBiYXNlIGxlYXJuZXJzIHdlcmUgKioqc3ZtLCBhbmQga3N2bSoqKiBhbmQgSSB1c2VkIGEgKioqbGFzc28gbW9kZWwqKiogYXMgb3VyIHN1cGVyLWxlYXJuZXI8L3A+CgpgYGB7ciwgZWNobz1GQUxTRX0Kc2V0LnNlZWQoMSkKYTwtYygic3ZtIiAsICJrc3ZtIiAsICAiU2FsZVByaWNlIikKc3RhY2tlZC5sYXNzbzwtbWFrZUxlYXJuZXIoInJlZ3IuZ2xtbmV0IiAsIGFscGhhID0gMSwgcGFyLnZhbHMgPSBsaXN0KHM9OTAwKSkKc3RhY2tlZC50cmFpbi50YXNrPC1tYWtlUmVnclRhc2soZGF0YSA9IHN0YWNrZWQuZGF0YS50cmFpbiAlPiUgc2VsZWN0KGEpLCB0YXJnZXQgPSAiU2FsZVByaWNlIikKdHVuZWQuc3RhY2tlZC5sYXNzbzwtdHJhaW4oc3RhY2tlZC5sYXNzbyAsIHN0YWNrZWQudHJhaW4udGFzaykKCmhvbGRvdXQgPC0gbWFrZVJlc2FtcGxlRGVzYyhtZXRob2QgPSAiQ1YiLCBpdGVycyA9IDUsIHN0cmF0aWZ5ID0gRkFMU0UsIHByZWRpY3QgPSAiYm90aCIpCmhvbGRvdXRDViA8LSByZXNhbXBsZShsZWFybmVyID0gc3RhY2tlZC5sYXNzbywgdGFzayA9IHN0YWNrZWQudHJhaW4udGFzaywgCiAgICAgICAgICAgICAgICAgICAgICByZXNhbXBsaW5nID0gaG9sZG91dCwgbWVhc3VyZXMgPSBsaXN0KHJtc2xlKSkKCiMgc2VsZWN0KGFbLWxlbmd0aChhKV0pCgpiYi50cmFpbi5zdGFja2VkPC1wcmVkaWN0KHR1bmVkLnN0YWNrZWQubGFzc28gLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gc3RhY2tlZC5kYXRhLnRyYWluICU+JSBzZWxlY3QoYVstbGVuZ3RoKGEpXSkpJGRhdGEkcmVzcG9uc2UKYmIudGVzdC5zdGFja2VkPC1wcmVkaWN0KHR1bmVkLnN0YWNrZWQubGFzc28gLCBuZXdkYXRhID0gc3RhY2tlZC5kYXRhLnRlc3QgJT4lIHNlbGVjdChhWy1sZW5ndGgoYSldKSkkZGF0YSRyZXNwb25zZQoKYGBgCgpgYGB7cn0Kc3RhY2tlZC5kYXRhLnRyYWluPC1hZGRfY29sdW1uKHN0YWNrZWQuZGF0YS50cmFpbiAsIHN0YWNrZWQubW9kZWwgPSBiYi50cmFpbi5zdGFja2VkKQpzdGFja2VkLmRhdGEudGVzdDwtYWRkX2NvbHVtbihzdGFja2VkLmRhdGEudGVzdCAsIHN0YWNrZWQubW9kZWwgPSBiYi50ZXN0LnN0YWNrZWQpCmBgYAoKCjxwIHN0eWxlPSJmb250LXNpemU6MzBweCI+IFN0YWNraW5nOiBCZW5jaG1hcmtpbmc8L3A+CgpgYGB7cn0Kc2V0LnNlZWQoMikKc3ZtLnJlZy5zY2FsZTwtbWFrZUxlYXJuZXIoInJlZ3Iuc3ZtIiAsIHBhci52YWxzID0gbGlzdChudSA9IDAuNSwgY29zdCA9IDMgLCB0b2xlcmFuY2UgPSAwLjAwMSksIGlkID0gInN2bS5zY2FsZSIpCmtzdm0ucmVnPC1tYWtlTGVhcm5lcigicmVnci5rc3ZtIiwgcGFyLnZhbHMgPSBsaXN0KEMgPSAyLCBlcHNpbG9uID0gMC4wMDEsIG51ID0gMC4yKSwgaWQgPSAia3N2bSIpCnN2bS5yZWcuMjwtbWFrZUxlYXJuZXIoInJlZ3Iuc3ZtIiwgcGFyLnZhbHMgPSBsaXN0KGNvc3QgPSA0LCB0b2xlcmFuY2UgPSAwLjAwNSAsIG51ID0gMC41LCBzY2FsZSA9IFRSVUUpICwgCiAgICAgICAgICAgICAgICAgICAgICAgaWQgPSAic3ZtLnJlZzIiKQpzdm0ucmVnPC1tYWtlTGVhcm5lcigicmVnci5zdm0iLCBwYXIudmFscyA9IGxpc3QoY29zdCA9IDQgLCBudSA9IDAuNSAsdG9sZXJhbmNlID0gMC4wMDEgKSwgaWQgPSAic3ZtIikKCgpscm5zID0gbGlzdChzdm0ucmVnLCBzdm0ucmVnLjIsIHN2bS5yZWcuc2NhbGUpCnRyYWluLnRhc2s8LW1ha2VSZWdyVGFzayhkYXRhID0gdHJhaW4uZGF0YSAlPiUgc2VsZWN0KGZlYXQpLCB0YXJnZXQgPSAiU2FsZVByaWNlIikKcmVwY3YgPSBtYWtlUmVzYW1wbGVEZXNjKCJSZXBDViIgLCBmb2xkcyA9IDMsIHJlcCA9IDUpCgpiMTwtYmVuY2htYXJrKGxybnMsIHRyYWluLnRhc2ssIHJlcGN2LCBtb2RlbHMgPSBUUlVFICwgbWVhc3VyZXMgPSBybXNsZSwgc2hvdy5pbmZvID0gRkFMU0UpCmBgYAoKCmBgYHtyLCBlY2hvPUZBTFNFfQpzZXQuc2VlZCgxKQpscm5zID0gbGlzdChzdm0ucmVnLnNjYWxlLCBrc3ZtLnJlZywgc3ZtLnJlZy4yLCBzdm0ucmVnLCB4Z2IsIGdsbS5ib29zdCwgbGFzc28uMSwgZWxhc3RpYy5uZXQpCnN0YWNrZWQubGVhcm5lci5tb2RlbC5sYXNzbzwtbWFrZVN0YWNrZWRMZWFybmVyKGJhc2UubGVhcm5lcnMgPSBscm5zLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VwZXIubGVhcm5lciA9IG1ha2VMZWFybmVyKCJyZWdyLmdsbW5ldCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWxwaGEgPSAxLCBwYXIudmFscyA9IGxpc3Qocz04MDApKSkKCmxybnMgPSBsaXN0KHhnYiwgZ2xtLmJvb3N0LCBzdGFja2VkLmxlYXJuZXIubW9kZWwubGFzc28pCnRyYWluLnRhc2s8LW1ha2VSZWdyVGFzayhkYXRhID0gbXV0YXRlX2FsbCh0cmFpbi5kYXRhICU+JSBzZWxlY3QoZmVhdCksIGFzLm51bWVyaWMpLCB0YXJnZXQgPSAiU2FsZVByaWNlIikKcmVwY3YgPSBtYWtlUmVzYW1wbGVEZXNjKCJSZXBDViIgLCBmb2xkcyA9IDMsIHJlcCA9IDUpCgpiMjwtYmVuY2htYXJrKGxybnMsIHRyYWluLnRhc2ssIHJlcGN2LCBtb2RlbHMgPSBUUlVFICwgbWVhc3VyZXMgPSBybXNsZSwgc2hvdy5pbmZvID0gRkFMU0UpCmBgYAoKCmBgYHtyLCBlY2hvPUZBTFNFfQpzZXQuc2VlZCgyKQpscm5zID0gbGlzdChsYXNzby4xKQp0cmFpbi50YXNrPC1tYWtlUmVnclRhc2soZGF0YSA9IHRyYWluLmRhdGEucm9vdCAlPiUgc2VsZWN0KGZlYXQpLCB0YXJnZXQgPSAiU2FsZVByaWNlIikKcmVwY3YgPSBtYWtlUmVzYW1wbGVEZXNjKCJSZXBDViIgLCBmb2xkcyA9IDMsIHJlcCA9IDUpCgpiMzwtYmVuY2htYXJrKGxybnMsIHRyYWluLnRhc2ssIHJlcGN2LCBtb2RlbHMgPSBUUlVFICwgbWVhc3VyZXMgPSBybXNsZSwgc2hvdy5pbmZvID0gRkFMU0UpCmBgYAoKCmBgYHtyLCBlY2hvPUZBTFNFfQpzZXQuc2VlZCgxKQpscm5zID0gbGlzdChlbGFzdGljLm5ldCkKdHJhaW4udGFzazwtbWFrZVJlZ3JUYXNrKGRhdGEgPSB0cmFpbi5kYXRhICwgdGFyZ2V0ID0gIlNhbGVQcmljZSIpCnJlcGN2ID0gbWFrZVJlc2FtcGxlRGVzYygiUmVwQ1YiICwgZm9sZHMgPSAzLCByZXAgPSA1KQoKYjQ8LWJlbmNobWFyayhscm5zLCB0cmFpbi50YXNrLCByZXBjdiwgbW9kZWxzID0gVFJVRSAsIG1lYXN1cmVzID0gcm1zbGUsIHNob3cuaW5mbyA9IEZBTFNFKQoKbHJucyA9IGxpc3Qoc3ZtLnJlZy4yKQp0cmFpbi50YXNrPC1tYWtlUmVnclRhc2soZGF0YSA9IHRyYWluLmRhdGEgJT4lIHNlbGVjdChpbXBvcnRhbnQubGFzc28uZmVhdHVyZXMpICwgdGFyZ2V0ID0gIlNhbGVQcmljZSIpCnJlcGN2ID0gbWFrZVJlc2FtcGxlRGVzYygiUmVwQ1YiICwgZm9sZHMgPSAzLCByZXAgPSA1KQoKYjU8LWJlbmNobWFyayhscm5zLCB0cmFpbi50YXNrLCByZXBjdiwgbW9kZWxzID0gVFJVRSAsIG1lYXN1cmVzID0gcm1zbGUsIHNob3cuaW5mbyA9IEZBTFNFKQpgYGAKCgpgYGB7ciwgZWNobz1GQUxTRX0KYjEuZGF0YTwtZ2V0Qk1SUGVyZm9ybWFuY2VzKGIxKSRgdHJhaW4uZGF0YSAlPiUgc2VsZWN0KGZlYXQpYApiMi5kYXRhPC1nZXRCTVJQZXJmb3JtYW5jZXMoYjIpJGBtdXRhdGVfYWxsKHRyYWluLmRhdGEgJT4lIHNlbGVjdChmZWF0KSwgYXMubnVtZXJpYylgCmIzLmRhdGE8LWdldEJNUlBlcmZvcm1hbmNlcyhiMykkYHRyYWluLmRhdGEucm9vdCAlPiUgc2VsZWN0KGZlYXQpYApiNC5kYXRhPC1nZXRCTVJQZXJmb3JtYW5jZXMoYjQpJHRyYWluLmRhdGEKYjUuZGF0YTwtZ2V0Qk1SUGVyZm9ybWFuY2VzKGI1KSRgdHJhaW4uZGF0YSAlPiUgc2VsZWN0KGltcG9ydGFudC5sYXNzby5mZWF0dXJlcylgCgpiZW5jaC5tYXJrLmRhdGE8LWRhdGEuZnJhbWUoc3ZtMS5mZWF0ID0gYjEuZGF0YSRzdm0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdm0yLmZlYXQgPSBiMS5kYXRhJHN2bS5yZWcyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgc3ZtMy5mZWF0ICA9IGIxLmRhdGEkc3ZtLnNjYWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgeGdib29zdC5mZWF0ID0gYjIuZGF0YSRyZWdyLnhnYm9vc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBnbG1ib29zdC5mZWF0ID0gYjIuZGF0YSRyZWdyLmdsbWJvb3N0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFzc28ubW9kZWwuc3RhY2tlZC5mZWF0ID0gYjIuZGF0YSRzdGFjaywKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhc3NvLmZlYXQgPWIzLmRhdGEkbGFzc28sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZS5uZXQuYWxsLnZhciA9IGI0LmRhdGEkZWxhc3RpYy5uZXQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdm00Lmxhc3NvLmZlYXR1cmVzID0gYjUuZGF0YSRzdm0ucmVnMikKCmBgYAoKPHAgc3R5bGU9ImZvbnQtc2l6ZTozMHB4Ij4gU3RhY2tpbmc6IEJlbmNobWFya2luZyBib3hwbG90czwvcD4KPHAgc3R5bGU9ImZvbnQtc2l6ZToxNXB4Ij4gV2Ugc2VlIHRoYXQgKioqYmVuY2htYXJraW5nKioqIGF2ZXJhZ2VkIHRoZSByZXN1bHRzIG9mIGFsbCB0aGUgTUwgbW9kZWxzPC9wPgo8cCBzdHlsZT0iZm9udC1zaXplOjE1cHgiPiBXZSBjYW4gY3JlYXRlIGFub3RoZXIgKioqc3RhY2tpbmcgbW9kZWwqKiogdG8gaW5jcmVhc2UgcGVyZm9ybWFuY2UsIG9yIHdlIGNhbiBtYW51YWxseSBhZGp1c3QgdGhlIHdlaWdodHMgdG8gc2VlIGhvdyB3ZWxsIHdlIGRvIGl0LjwvcD4KCmBgYHtyLCBlY2hvPUZBTFNFfQpzLmRmPC1yZXNoYXBlMjo6bWVsdChiZW5jaC5tYXJrLmRhdGEgJT4lIHNlbGVjdF9pZihpcy5kb3VibGUpKQpnZ3Bsb3QoZGF0YSA9IHMuZGYsIGFlcyh4ID0gdmFyaWFibGUgLCB5ID0gdmFsdWUpKSArIGdlb21fYm94cGxvdCgpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUsIGhqdXN0PTEpKSArIGdndGl0bGUoIkJveHBsb3Qgb2YgZWFjaCBNTCBtb2RlbHMiKQpgYGAKCjxwIHN0eWxlPSJmb250LXNpemU6MzBweCI+IFN0YWNraW5nOiBNYW51ZWwgd2VpZ2h0cyBiYXNlZCBvbiBSTVNMRTwvcD4KCmBgYHtyLCBlY2hvPUZBTFNFfQpybXNsZS5sb3NzPC1jKCkKZm9yKGkgaW4gMTo1KXsKICBzZXQuc2VlZChpKQogIGxpYnJhcnkoc3BsaXRUb29scykKICBteS5kYXRhLnNwbGl0PC1zdGFja2VkLmRhdGEudHJhaW4gJT4lIHNlbGVjdChjKCJzdm0iLCJrc3ZtIiwgICJnbG0uYm9vc3QiLCAic3RhY2tlZC5tb2RlbCIsICJTYWxlUHJpY2UiKSkKICBpbmRleDwtcGFydGl0aW9uKHN0YWNrZWQuZGF0YS50cmFpbiRTYWxlUHJpY2UgLCBwID0gYyh0cmFpbiA9IDAuMywgdmFsaWQgPSAwLjMsIHRlc3QgPSAwLjMpKQogIGFhPC1teS5kYXRhLnNwbGl0W2luZGV4JHRyYWluLF0KICBiYjwtbXkuZGF0YS5zcGxpdFtpbmRleCR2YWxpZCxdCiAgY2M8LW15LmRhdGEuc3BsaXRbaW5kZXgkdGVzdCxdCgogIGE8LTAuNgogIGI8LTAKICBjPC0wLjIKICBkPC0wLjcKICAKCiAgcHAuMTwtYXBwbHkoYWEgJT4lIHNlbGVjdCgtU2FsZVByaWNlKSAsIDEsIAogICAgICAgICAgICBmdW5jdGlvbih4KXtyZXR1cm4od2VpZ2h0ZWQubWVhbih4LCB3ID0gYyhhLGIsYyxkKSkpfSkKCiAgcHAuMjwtYXBwbHkoYmIgJT4lIHNlbGVjdCgtU2FsZVByaWNlKSAsIDEsIAogICAgICAgICAgICBmdW5jdGlvbih4KXtyZXR1cm4od2VpZ2h0ZWQubWVhbih4LCB3ID0gYyhhLGIsYyxkKSkpfSkKCiAgcHAuMzwtYXBwbHkoY2MgJT4lIHNlbGVjdCgtU2FsZVByaWNlKSAsIDEsIAogICAgICAgICAgICBmdW5jdGlvbih4KXtyZXR1cm4od2VpZ2h0ZWQubWVhbih4LCB3ID0gYyhhLGIsYyxkKSkpfSkKCiAgcm1zbGUuMTwtc3FydChtZWFuKChsb2cocHAuMSArMSkgLSBsb2coYWEkU2FsZVByaWNlKSleMikpCiAgcm1zbGUuMjwtc3FydChtZWFuKChsb2cocHAuMiArMSkgLSBsb2coYmIkU2FsZVByaWNlKSleMikpCiAgcm1zbGUuMzwtc3FydChtZWFuKChsb2cocHAuMyArMSkgLSBsb2coY2MkU2FsZVByaWNlKSleMikpCgogIHJtc2xlLmxvc3M8LWMocm1zbGUubG9zcyAscm1zbGUuMSAsIHJtc2xlLjIgLCBybXNsZS4zICkKfQpgYGAKCgo8cCBzdHlsZT0iZm9udC1zaXplOjE1cHgiPiBJdCBpcyB2ZXJ5IGV2aWRlbnQgdGhhdCB0aGUgY3VzdG9tIHdlaWdodHMgd2UgaGF2ZSBjcmVhdGVkIGdhdmUgdXMgdGhlIGdyZWF0ZXN0IG92ZXJhbGwgcGVyZm9ybWFuY2UuIFdlIHdpbGwgdXNlIHRoZXNlIGN1c3RvbSB3ZWlnaHRzIGZvciByZXByZXNlbnQgb3VyIHRlc3QgZGF0YSBhcyB3ZWxsLjwvcD4KCmBgYHtyLCBlY2hvPUZBTFNFfQpiZW5jaC5tYXJrLmRhdGE8LWFkZF9jb2x1bW4oYmVuY2gubWFyay5kYXRhICwgY3VzdG9tLndlaWdodHMgPXJtc2xlLmxvc3MgKQpzLmRmPC1yZXNoYXBlMjo6bWVsdChiZW5jaC5tYXJrLmRhdGEgJT4lIHNlbGVjdF9pZihpcy5kb3VibGUpKQpnZ3Bsb3QoZGF0YSA9IHMuZGYsIGFlcyh4ID0gdmFyaWFibGUgLCB5ID0gdmFsdWUpKSArIGdlb21fYm94cGxvdCgpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUsIGhqdXN0PTEpKQpgYGAKCgo8cD5UaGUgZmluYWwgcHJlZGljdGlvbnM8L3A+CmBgYHtyLCBlY2hvPVRSVUV9Cgp6PC1zdGFja2VkLmRhdGEudGVzdCAlPiUgc2VsZWN0KGMoInN2bSIsImtzdm0iLCAgImdsbS5ib29zdCIsICJzdGFja2VkLm1vZGVsIikpCgphPC0wLjYKYjwtMApjPC0wLjIKZDwtMC43CnBwPC1hcHBseSh6ICwgMSwgZnVuY3Rpb24oeCl7cmV0dXJuKHdlaWdodGVkLm1lYW4oeCwgdyA9IGMoYSxiLGMsZCkpKX0pCnByaW50KGhlYWQocHApKQpgYGAKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoK